From d02b74a5dcfed4bfc8f2f8e545bca4d2afabb296 Mon Sep 17 00:00:00 2001 From: Googler Date: Fri, 27 Apr 2018 10:37:02 -0700 Subject: Check in gVisor. PiperOrigin-RevId: 194583126 Change-Id: Ica1d8821a90f74e7e745962d71801c598c652463 --- runsc/specutils/BUILD | 18 +++++ runsc/specutils/specutils.go | 183 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 runsc/specutils/BUILD create mode 100644 runsc/specutils/specutils.go (limited to 'runsc/specutils') diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD new file mode 100644 index 000000000..ae89260d2 --- /dev/null +++ b/runsc/specutils/BUILD @@ -0,0 +1,18 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "specutils", + srcs = ["specutils.go"], + importpath = "gvisor.googlesource.com/gvisor/runsc/specutils", + visibility = [ + "//runsc:__subpackages__", + ], + deps = [ + "//pkg/abi/linux", + "//pkg/log", + "//pkg/sentry/kernel/auth", + "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + ], +) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go new file mode 100644 index 000000000..bed0f75eb --- /dev/null +++ b/runsc/specutils/specutils.go @@ -0,0 +1,183 @@ +// Copyright 2018 Google Inc. +// +// 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 specutils contains utility functions for working with OCI runtime +// specs. +package specutils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" +) + +// LogSpec logs the spec in a human-friendly way. +func LogSpec(spec *specs.Spec) { + log.Debugf("Spec: %+v", spec) + log.Debugf("Spec.Hooks: %+v", spec.Hooks) + log.Debugf("Spec.Linux: %+v", spec.Linux) + log.Debugf("Spec.Process: %+v", spec.Process) + log.Debugf("Spec.Root: %+v", spec.Root) +} + +// ReadSpec reads an OCI runtime spec from the given bundle directory. +// +// TODO: This should validate the spec. +func ReadSpec(bundleDir string) (*specs.Spec, error) { + // The spec file must be in "config.json" inside the bundle directory. + specFile := filepath.Join(bundleDir, "config.json") + specBytes, err := ioutil.ReadFile(specFile) + if err != nil { + return nil, fmt.Errorf("error reading spec from file %q: %v", specFile, err) + } + var spec specs.Spec + if err := json.Unmarshal(specBytes, &spec); err != nil { + return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile, err, string(specBytes)) + } + return &spec, nil +} + +// GetExecutablePath returns the absolute path to the executable, relative to +// the root. It searches the environment PATH for the first file that exists +// with the given name. +func GetExecutablePath(exec, root string, env []string) (string, error) { + exec = filepath.Clean(exec) + + // Don't search PATH if exec is a path to a file (absolute or relative). + if strings.IndexByte(exec, '/') >= 0 { + return exec, nil + } + + // Get the PATH from the environment. + const prefix = "PATH=" + var path []string + for _, e := range env { + if strings.HasPrefix(e, prefix) { + path = strings.Split(strings.TrimPrefix(e, prefix), ":") + break + } + } + + // Search the PATH for a file whose name matches the one we are looking + // for. + for _, p := range path { + abs := filepath.Join(root, p, exec) + if _, err := os.Stat(abs); err == nil { + // We found it! Return the path relative to the root. + return filepath.Join("/", p, exec), nil + } + } + + // Could not find a suitable path, just return the original string. + log.Warningf("could not find executable %s in path %s", exec, path) + return exec, nil +} + +// Capabilities takes in spec and returns a TaskCapabilities corresponding to +// the spec. +func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) { + var caps auth.TaskCapabilities + if specCaps != nil { + var err error + if caps.BoundingCaps, err = capsFromNames(specCaps.Bounding); err != nil { + return nil, err + } + if caps.EffectiveCaps, err = capsFromNames(specCaps.Effective); err != nil { + return nil, err + } + if caps.InheritableCaps, err = capsFromNames(specCaps.Inheritable); err != nil { + return nil, err + } + if caps.PermittedCaps, err = capsFromNames(specCaps.Permitted); err != nil { + return nil, err + } + // TODO: Support ambient capabilities. + } + return &caps, nil +} + +var capFromName = map[string]linux.Capability{ + "CAP_CHOWN": linux.CAP_CHOWN, + "CAP_DAC_OVERRIDE": linux.CAP_DAC_OVERRIDE, + "CAP_DAC_READ_SEARCH": linux.CAP_DAC_READ_SEARCH, + "CAP_FOWNER": linux.CAP_FOWNER, + "CAP_FSETID": linux.CAP_FSETID, + "CAP_KILL": linux.CAP_KILL, + "CAP_SETGID": linux.CAP_SETGID, + "CAP_SETUID": linux.CAP_SETUID, + "CAP_SETPCAP": linux.CAP_SETPCAP, + "CAP_LINUX_IMMUTABLE": linux.CAP_LINUX_IMMUTABLE, + "CAP_NET_BIND_SERVICE": linux.CAP_NET_BIND_SERVICE, + "CAP_NET_BROAD_CAST": linux.CAP_NET_BROAD_CAST, + "CAP_NET_ADMIN": linux.CAP_NET_ADMIN, + "CAP_NET_RAW": linux.CAP_NET_RAW, + "CAP_IPC_LOCK": linux.CAP_IPC_LOCK, + "CAP_IPC_OWNER": linux.CAP_IPC_OWNER, + "CAP_SYS_MODULE": linux.CAP_SYS_MODULE, + "CAP_SYS_RAWIO": linux.CAP_SYS_RAWIO, + "CAP_SYS_CHROOT": linux.CAP_SYS_CHROOT, + "CAP_SYS_PTRACE": linux.CAP_SYS_PTRACE, + "CAP_SYS_PACCT": linux.CAP_SYS_PACCT, + "CAP_SYS_ADMIN": linux.CAP_SYS_ADMIN, + "CAP_SYS_BOOT": linux.CAP_SYS_BOOT, + "CAP_SYS_NICE": linux.CAP_SYS_NICE, + "CAP_SYS_RESOURCE": linux.CAP_SYS_RESOURCE, + "CAP_SYS_TIME": linux.CAP_SYS_TIME, + "CAP_SYS_TTY_CONFIG": linux.CAP_SYS_TTY_CONFIG, + "CAP_MKNOD": linux.CAP_MKNOD, + "CAP_LEASE": linux.CAP_LEASE, + "CAP_AUDIT_WRITE": linux.CAP_AUDIT_WRITE, + "CAP_AUDIT_CONTROL": linux.CAP_AUDIT_CONTROL, + "CAP_SETFCAP": linux.CAP_SETFCAP, + "CAP_MAC_OVERRIDE": linux.CAP_MAC_OVERRIDE, + "CAP_MAC_ADMIN": linux.CAP_MAC_ADMIN, + "CAP_SYSLOG": linux.CAP_SYSLOG, + "CAP_WAKE_ALARM": linux.CAP_WAKE_ALARM, + "CAP_BLOCK_SUSPEND": linux.CAP_BLOCK_SUSPEND, +} + +func capsFromNames(names []string) (auth.CapabilitySet, error) { + var caps []linux.Capability + for _, n := range names { + c, ok := capFromName[n] + if !ok { + return 0, fmt.Errorf("unknown capability %q", n) + } + caps = append(caps, c) + } + return auth.CapabilitySetOfMany(caps), nil +} + +// Is9PMount returns true if the given mount can be mounted as an external gofer. +func Is9PMount(m specs.Mount) bool { + return m.Type == "bind" && m.Source != "" && !strings.HasPrefix(m.Destination, "/dev") +} + +// BinPath returns the real path to self, resolving symbolink links. This is done +// to make the process name appears as 'runsc', instead of 'exe'. +func BinPath() (string, error) { + binPath, err := filepath.EvalSymlinks("/proc/self/exe") + if err != nil { + return "", fmt.Errorf(`error resolving "/proc/self/exe" symlink: %v`, err) + } + return binPath, nil +} -- cgit v1.2.3 From c186ebb62a6005288d83feed0e43cca9f0577383 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 3 May 2018 21:08:38 -0700 Subject: Return error when child exits early PiperOrigin-RevId: 195365050 Change-Id: I8754dc7a3fc2975d422cae453762a455478a8e6a --- runsc/cmd/exec.go | 87 ++++++++++++++++++----------------- runsc/sandbox/sandbox.go | 23 +++++----- runsc/specutils/BUILD | 9 +++- runsc/specutils/specutils.go | 32 +++++++++++++ runsc/specutils/specutils_test.go | 96 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 193 insertions(+), 54 deletions(-) create mode 100644 runsc/specutils/specutils_test.go (limited to 'runsc/specutils') diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index 576031b5b..052e00316 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -123,48 +123,7 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) // write the child's PID to the pid file. So when the container returns, the // child process will also return and signal containerd. if ex.detach { - binPath, err := specutils.BinPath() - if err != nil { - Fatalf("error getting bin path: %v", err) - } - var args []string - for _, a := range os.Args[1:] { - if !strings.Contains(a, "detach") { - args = append(args, a) - } - } - cmd := exec.Command(binPath, args...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Start(); err != nil { - Fatalf("failure to start child exec process, err: %v", err) - } - - log.Infof("Started child (PID: %d) to exec and wait: %s %s", cmd.Process.Pid, binPath, args) - - // Wait for PID file to ensure that child process has started. Otherwise, - // '--process' file is deleted as soon as this process returns and the child - // may fail to read it. - sleepTime := 10 * time.Millisecond - for start := time.Now(); time.Now().Sub(start) < 10*time.Second; { - _, err := os.Stat(ex.pidFile) - if err == nil { - break - } - if pe, ok := err.(*os.PathError); !ok || pe.Err != syscall.ENOENT { - Fatalf("unexpected error waiting for PID file, err: %v", err) - } - - log.Infof("Waiting for PID file to be created...") - time.Sleep(sleepTime) - sleepTime *= sleepTime * 2 - if sleepTime > 1*time.Second { - sleepTime = 1 * time.Second - } - } - *waitStatus = 0 - return subcommands.ExitSuccess + return ex.execAndWait(waitStatus) } if ex.pidFile != "" { @@ -191,6 +150,50 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) return subcommands.ExitSuccess } +func (ex *Exec) execAndWait(waitStatus *syscall.WaitStatus) subcommands.ExitStatus { + binPath, err := specutils.BinPath() + if err != nil { + Fatalf("error getting bin path: %v", err) + } + var args []string + for _, a := range os.Args[1:] { + if !strings.Contains(a, "detach") { + args = append(args, a) + } + } + cmd := exec.Command(binPath, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + Fatalf("failure to start child exec process, err: %v", err) + } + + log.Infof("Started child (PID: %d) to exec and wait: %s %s", cmd.Process.Pid, binPath, args) + + // Wait for PID file to ensure that child process has started. Otherwise, + // '--process' file is deleted as soon as this process returns and the child + // may fail to read it. + ready := func() (bool, error) { + _, err := os.Stat(ex.pidFile) + if err == nil { + // File appeared, we're done! + return true, nil + } + if pe, ok := err.(*os.PathError); !ok || pe.Err != syscall.ENOENT { + return false, err + } + // No file yet, continue to wait... + return false, nil + } + if err := specutils.WaitForReady(cmd.Process.Pid, 10*time.Second, ready); err != nil { + Fatalf("unexpected error waiting for PID file, err: %v", err) + } + + *waitStatus = 0 + return subcommands.ExitSuccess +} + // parseArgs parses exec information from the command line or a JSON file // depending on whether the --process flag was used. Returns an ExecArgs and // the ID of the sandbox to be used. diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 954824ada..13bf5d800 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -560,19 +560,20 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common // running, at which point the sandbox is in Created state. func (s *Sandbox) waitForCreated(timeout time.Duration) error { log.Debugf("Waiting for sandbox %q creation", s.ID) - tchan := time.After(timeout) - for { - select { - case <-tchan: - return fmt.Errorf("timed out waiting for sandbox control server") - default: - if c, err := client.ConnectTo(boot.ControlSocketAddr(s.ID)); err == nil { - // It's alive! - c.Close() - return nil - } + + ready := func() (bool, error) { + c, err := client.ConnectTo(boot.ControlSocketAddr(s.ID)) + if err != nil { + return false, nil } + // It's alive! + c.Close() + return true, nil } + if err := specutils.WaitForReady(s.Pid, timeout, ready); err != nil { + return fmt.Errorf("unexpected error waiting for sandbox %q, err: %v", s.ID, err) + } + return nil } // Wait waits for the containerized process to exit, and returns its WaitStatus. diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index ae89260d2..1b6d265bc 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -1,6 +1,6 @@ package(licenses = ["notice"]) # Apache 2.0 -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "specutils", @@ -16,3 +16,10 @@ go_library( "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", ], ) + +go_test( + name = "specutils_test", + size = "small", + srcs = ["specutils_test.go"], + embed = [":specutils"], +) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index bed0f75eb..04ecb6ae3 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -23,6 +23,8 @@ import ( "os" "path/filepath" "strings" + "syscall" + "time" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.googlesource.com/gvisor/pkg/abi/linux" @@ -181,3 +183,33 @@ func BinPath() (string, error) { } return binPath, nil } + +// WaitForReady waits for a process to become ready. The process is ready when +// the 'ready' function returns true. It continues to wait if 'ready' returns +// false. It returns error on timeout, if the process stops or if 'ready' fails. +func WaitForReady(pid int, timeout time.Duration, ready func() (bool, error)) error { + backoff := 1 * time.Millisecond + for start := time.Now(); time.Now().Sub(start) < timeout; { + if ok, err := ready(); err != nil { + return err + } else if ok { + return nil + } + + // Check if the process is still running. + var ws syscall.WaitStatus + var ru syscall.Rusage + child, err := syscall.Wait4(pid, &ws, syscall.WNOHANG, &ru) + if err != nil || child == pid { + return fmt.Errorf("process (%d) is not running, err: %v", pid, err) + } + + // Process continues to run, backoff and retry. + time.Sleep(backoff) + backoff *= 2 + if backoff > 1*time.Second { + backoff = 1 * time.Second + } + } + return fmt.Errorf("timed out waiting for process (%d)", pid) +} diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go new file mode 100644 index 000000000..ef293e608 --- /dev/null +++ b/runsc/specutils/specutils_test.go @@ -0,0 +1,96 @@ +// Copyright 2018 Google Inc. +// +// 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 specutils + +import ( + "fmt" + "os/exec" + "strings" + "testing" + "time" +) + +func TestWaitForReadyHappy(t *testing.T) { + cmd := exec.Command("/bin/sleep", "1000") + if err := cmd.Start(); err != nil { + t.Fatalf("cmd.Start() failed, err: %v", err) + } + defer cmd.Wait() + + var count int + err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) { + if count < 3 { + count++ + return false, nil + } + return true, nil + }) + if err != nil { + t.Errorf("ProcessWaitReady got: %v, expected: nil", err) + } + cmd.Process.Kill() +} + +func TestWaitForReadyFail(t *testing.T) { + cmd := exec.Command("/bin/sleep", "1000") + if err := cmd.Start(); err != nil { + t.Fatalf("cmd.Start() failed, err: %v", err) + } + defer cmd.Wait() + + var count int + err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) { + if count < 3 { + count++ + return false, nil + } + return false, fmt.Errorf("Fake error") + }) + if err == nil { + t.Errorf("ProcessWaitReady got: nil, expected: error") + } + cmd.Process.Kill() +} + +func TestWaitForReadyNotRunning(t *testing.T) { + cmd := exec.Command("/bin/true") + if err := cmd.Start(); err != nil { + t.Fatalf("cmd.Start() failed, err: %v", err) + } + defer cmd.Wait() + + err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) { + return false, nil + }) + if !strings.Contains(err.Error(), "not running") { + t.Errorf("ProcessWaitReady got: %v, expected: not running", err) + } +} + +func TestWaitForReadyTimeout(t *testing.T) { + cmd := exec.Command("/bin/sleep", "1000") + if err := cmd.Start(); err != nil { + t.Fatalf("cmd.Start() failed, err: %v", err) + } + defer cmd.Wait() + + err := WaitForReady(cmd.Process.Pid, 50*time.Millisecond, func() (bool, error) { + return false, nil + }) + if !strings.Contains(err.Error(), "timed out") { + t.Errorf("ProcessWaitReady got: %v, expected: timed out", err) + } + cmd.Process.Kill() +} -- cgit v1.2.3 From c90fefc1161c58af34856aff7b7012f19f5d1f1b Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Fri, 4 May 2018 09:38:35 -0700 Subject: Fix runsc capabilities There was a typo and one new capability missing from the list PiperOrigin-RevId: 195427713 Change-Id: I6d9e1c6e77b48fe85ef10d9f54c70c8a7271f6e7 --- pkg/abi/linux/capability.go | 5 +++-- runsc/boot/capability.go | 3 ++- runsc/specutils/specutils.go | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'runsc/specutils') diff --git a/pkg/abi/linux/capability.go b/pkg/abi/linux/capability.go index 1a1bd0ce3..b470ce0a5 100644 --- a/pkg/abi/linux/capability.go +++ b/pkg/abi/linux/capability.go @@ -32,7 +32,7 @@ const ( CAP_SETPCAP = Capability(8) CAP_LINUX_IMMUTABLE = Capability(9) CAP_NET_BIND_SERVICE = Capability(10) - CAP_NET_BROAD_CAST = Capability(11) + CAP_NET_BROADCAST = Capability(11) CAP_NET_ADMIN = Capability(12) CAP_NET_RAW = Capability(13) CAP_IPC_LOCK = Capability(14) @@ -58,9 +58,10 @@ const ( CAP_SYSLOG = Capability(34) CAP_WAKE_ALARM = Capability(35) CAP_BLOCK_SUSPEND = Capability(36) + CAP_AUDIT_READ = Capability(37) // MaxCapability is the highest-numbered capability. - MaxCapability = Capability(36) // CAP_BLOCK_SUSPEND as of 3.11 + MaxCapability = CAP_AUDIT_READ ) // Ok returns true if cp is a supported capability. diff --git a/runsc/boot/capability.go b/runsc/boot/capability.go index 4c6a59245..efa28fb97 100644 --- a/runsc/boot/capability.go +++ b/runsc/boot/capability.go @@ -91,7 +91,7 @@ var capFromName = map[string]capability.Cap{ "CAP_SETPCAP": capability.CAP_SETPCAP, "CAP_LINUX_IMMUTABLE": capability.CAP_LINUX_IMMUTABLE, "CAP_NET_BIND_SERVICE": capability.CAP_NET_BIND_SERVICE, - "CAP_NET_BROAD_CAST": capability.CAP_NET_BROADCAST, + "CAP_NET_BROADCAST": capability.CAP_NET_BROADCAST, "CAP_NET_ADMIN": capability.CAP_NET_ADMIN, "CAP_NET_RAW": capability.CAP_NET_RAW, "CAP_IPC_LOCK": capability.CAP_IPC_LOCK, @@ -117,4 +117,5 @@ var capFromName = map[string]capability.Cap{ "CAP_SYSLOG": capability.CAP_SYSLOG, "CAP_WAKE_ALARM": capability.CAP_WAKE_ALARM, "CAP_BLOCK_SUSPEND": capability.CAP_BLOCK_SUSPEND, + "CAP_AUDIT_READ": capability.CAP_AUDIT_READ, } diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 04ecb6ae3..dcb4b20db 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -129,7 +129,7 @@ var capFromName = map[string]linux.Capability{ "CAP_SETPCAP": linux.CAP_SETPCAP, "CAP_LINUX_IMMUTABLE": linux.CAP_LINUX_IMMUTABLE, "CAP_NET_BIND_SERVICE": linux.CAP_NET_BIND_SERVICE, - "CAP_NET_BROAD_CAST": linux.CAP_NET_BROAD_CAST, + "CAP_NET_BROADCAST": linux.CAP_NET_BROADCAST, "CAP_NET_ADMIN": linux.CAP_NET_ADMIN, "CAP_NET_RAW": linux.CAP_NET_RAW, "CAP_IPC_LOCK": linux.CAP_IPC_LOCK, @@ -155,6 +155,7 @@ var capFromName = map[string]linux.Capability{ "CAP_SYSLOG": linux.CAP_SYSLOG, "CAP_WAKE_ALARM": linux.CAP_WAKE_ALARM, "CAP_BLOCK_SUSPEND": linux.CAP_BLOCK_SUSPEND, + "CAP_AUDIT_READ": linux.CAP_AUDIT_READ, } func capsFromNames(names []string) (auth.CapabilitySet, error) { -- cgit v1.2.3 From 205f1027e6beb84101439172b3c776c2671b5be8 Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Tue, 15 May 2018 10:17:19 -0700 Subject: Refactor the Sandbox package into Sandbox + Container. This is a necessary prerequisite for supporting multiple containers in a single sandbox. All the commands (in cmd package) now call operations on Containers (container package). When a Container first starts, it will create a Sandbox with the same ID. The Sandbox class is now simpler, as it only knows how to create boot/gofer processes, and how to forward commands into the running boot process. There are TODOs sprinkled around for additional support for multiple containers. Most notably, we need to detect when a container is intended to run in an existing sandbox (by reading the metadata), and then have some way to signal to the sandbox to start a new container. Other urpc calls into the sandbox need to pass the container ID, so the sandbox can run the operation on the given container. These are only half-plummed through right now. PiperOrigin-RevId: 196688269 Change-Id: I1ecf4abbb9dd8987a53ae509df19341aaf42b5b0 --- runsc/boot/config.go | 46 ++- runsc/cmd/BUILD | 2 +- runsc/cmd/cmd.go | 11 - runsc/cmd/create.go | 17 +- runsc/cmd/delete.go | 18 +- runsc/cmd/events.go | 8 +- runsc/cmd/exec.go | 20 +- runsc/cmd/kill.go | 10 +- runsc/cmd/list.go | 34 +- runsc/cmd/ps.go | 8 +- runsc/cmd/run.go | 6 +- runsc/cmd/start.go | 10 +- runsc/cmd/state.go | 16 +- runsc/container/BUILD | 45 +++ runsc/container/container.go | 380 ++++++++++++++++++++++ runsc/container/container_test.go | 669 ++++++++++++++++++++++++++++++++++++++ runsc/container/hook.go | 111 +++++++ runsc/container/status.go | 54 +++ runsc/main.go | 4 + runsc/sandbox/BUILD | 25 +- runsc/sandbox/hook.go | 111 ------- runsc/sandbox/sandbox.go | 430 ++++++------------------ runsc/sandbox/sandbox_test.go | 665 ------------------------------------- runsc/sandbox/status.go | 56 ---- runsc/specutils/specutils.go | 23 +- 25 files changed, 1499 insertions(+), 1280 deletions(-) create mode 100644 runsc/container/BUILD create mode 100644 runsc/container/container.go create mode 100644 runsc/container/container_test.go create mode 100644 runsc/container/hook.go create mode 100644 runsc/container/status.go delete mode 100644 runsc/sandbox/hook.go delete mode 100644 runsc/sandbox/sandbox_test.go delete mode 100644 runsc/sandbox/status.go (limited to 'runsc/specutils') diff --git a/runsc/boot/config.go b/runsc/boot/config.go index f3e33e89a..d5dd400d1 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -14,7 +14,11 @@ package boot -import "fmt" +import ( + "fmt" + "strconv" + "strings" +) // PlatformType tells which platform to use. type PlatformType int @@ -131,6 +135,19 @@ type Config struct { // RootDir is the runtime root directory. RootDir string + // Debug indicates that debug logging should be enabled. + Debug bool + + // LogFilename is the filename to log to, if not empty. + LogFilename string + + // LogFormat is the log format, "text" or "json". + LogFormat string + + // DebugLogDir is the directory to log debug information to, if not + // empty. + DebugLogDir string + // FileAccess indicates how the filesystem is accessed. FileAccess FileAccessType @@ -159,4 +176,31 @@ type Config struct { // DisableSeccomp indicates whether seccomp syscall filters should be // disabled. Pardon the double negation, but default to enabled is important. DisableSeccomp bool + + // TestModeNoFlags indicates that the ToFlags method should return + // empty. This should only be used in tests, since the test runner does + // not know about all the flags. + TestModeNoFlags bool +} + +// ToFlags returns a slice of flags that correspond to the given Config. +func (c *Config) ToFlags() []string { + if c.TestModeNoFlags { + return nil + } + return []string{ + "--root=" + c.RootDir, + "--debug=" + strconv.FormatBool(c.Debug), + "--log=" + c.LogFilename, + "--log-format=" + c.LogFormat, + "--debug-log-dir=" + c.DebugLogDir, + "--file-access=" + c.FileAccess.String(), + "--overlay=" + strconv.FormatBool(c.Overlay), + "--network=" + c.Network.String(), + "--log-packets=" + strconv.FormatBool(c.LogPackets), + "--platform=" + c.Platform.String(), + "--strace=" + strconv.FormatBool(c.Strace), + "--strace-syscalls=" + strings.Join(c.StraceSyscalls, ","), + "--strace-log-size=" + strconv.Itoa(int(c.StraceLogSize)), + } } diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 128c8f7e6..08aaee996 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -32,8 +32,8 @@ go_library( "//pkg/unet", "//pkg/urpc", "//runsc/boot", + "//runsc/container", "//runsc/fsgofer", - "//runsc/sandbox", "//runsc/specutils", "@com_github_google_subcommands//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", diff --git a/runsc/cmd/cmd.go b/runsc/cmd/cmd.go index d4b834213..9f7fd6e25 100644 --- a/runsc/cmd/cmd.go +++ b/runsc/cmd/cmd.go @@ -20,7 +20,6 @@ import ( "os" "strconv" - "flag" "gvisor.googlesource.com/gvisor/pkg/log" ) @@ -35,16 +34,6 @@ func Fatalf(s string, args ...interface{}) { os.Exit(128) } -// commandLineFlags returns a slice of all top-level command line flags that -// have been set. -func commandLineFlags() []string { - var args []string - flag.CommandLine.Visit(func(f *flag.Flag) { - args = append(args, fmt.Sprintf("--%s=%s", f.Name, f.Value.String())) - }) - return args -} - // intFlags can be used with int flags that appear multiple times. type intFlags []int diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 83cb09eb0..94a889077 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -19,7 +19,7 @@ import ( "flag" "github.com/google/subcommands" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -30,8 +30,8 @@ type Create struct { bundleDir string // pidFile is the filename that the sandbox pid will be written to. - // This file should only be created once the sandbox process is ready - // to use (i.e. control server has started and is listening). + // This file should only be created once the container process inside + // the sandbox is ready to use. pidFile string // consoleSocket is the path to an AF_UNIX socket which will receive a @@ -61,7 +61,7 @@ func (*Create) Usage() string { func (c *Create) SetFlags(f *flag.FlagSet) { f.StringVar(&c.bundleDir, "bundle", "", "path to the root of the bundle directory, defaults to the current directory") f.StringVar(&c.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.StringVar(&c.pidFile, "pid-file", "", "filename that the sandbox pid will be written to") + f.StringVar(&c.pidFile, "pid-file", "", "filename that the container pid will be written to") } // Execute implements subcommands.Command.Execute. @@ -84,10 +84,11 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} } specutils.LogSpec(spec) - // Create the sandbox process, passing additional command line - // arguments to the sandbox process. - if _, err := sandbox.Create(id, spec, conf, bundleDir, c.consoleSocket, c.pidFile, commandLineFlags()); err != nil { - Fatalf("error creating sandbox: %v", err) + // Create the container. A new sandbox will be created for the + // container unless the metadata specifies that it should be run in an + // existing container. + if _, err := container.Create(id, spec, conf, bundleDir, c.consoleSocket, c.pidFile); err != nil { + Fatalf("error creating container: %v", err) } return subcommands.ExitSuccess } diff --git a/runsc/cmd/delete.go b/runsc/cmd/delete.go index a497c034d..769a11c45 100644 --- a/runsc/cmd/delete.go +++ b/runsc/cmd/delete.go @@ -19,12 +19,12 @@ import ( "flag" "github.com/google/subcommands" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" ) // Delete implements subcommands.Command for the "delete" command. type Delete struct { - // force indicates that the sandbox should be terminated if running. + // force indicates that the container should be terminated if running. force bool } @@ -45,7 +45,7 @@ func (*Delete) Usage() string { // SetFlags implements subcommands.Command.SetFlags. func (d *Delete) SetFlags(f *flag.FlagSet) { - f.BoolVar(&d.force, "force", false, "terminate sandbox if running") + f.BoolVar(&d.force, "force", false, "terminate container if running") } // Execute implements subcommands.Command.Execute. @@ -59,15 +59,15 @@ func (d *Delete) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} for i := 0; i < f.NArg(); i++ { id := f.Arg(i) - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandbox %q: %v", id, err) + Fatalf("error loading container %q: %v", id, err) } - if !d.force && (s.Status == sandbox.Running) { - Fatalf("cannot stop running sandbox without --force flag") + if !d.force && (c.Status == container.Running) { + Fatalf("cannot stop running container without --force flag") } - if err := s.Destroy(); err != nil { - Fatalf("error destroying sandbox: %v", err) + if err := c.Destroy(); err != nil { + Fatalf("error destroying container: %v", err) } } return subcommands.ExitSuccess diff --git a/runsc/cmd/events.go b/runsc/cmd/events.go index afd42c2f2..f221ad3ae 100644 --- a/runsc/cmd/events.go +++ b/runsc/cmd/events.go @@ -24,7 +24,7 @@ import ( "github.com/google/subcommands" "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" ) // Events implements subcommands.Command for the "events" command. @@ -74,7 +74,7 @@ func (evs *Events) Execute(ctx context.Context, f *flag.FlagSet, args ...interfa id := f.Arg(0) conf := args[0].(*boot.Config) - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { Fatalf("error loading sandox: %v", err) } @@ -82,9 +82,9 @@ func (evs *Events) Execute(ctx context.Context, f *flag.FlagSet, args ...interfa // Repeatedly get stats from the container. for { // Get the event and print it as JSON. - ev, err := s.Event() + ev, err := c.Event() if err != nil { - log.Warningf("error getting events for sandbox: %v", err) + log.Warningf("error getting events for container: %v", err) } // err must be preserved because it is used below when breaking // out of the loop. diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index 052e00316..235ed9bc6 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -34,7 +34,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" "gvisor.googlesource.com/gvisor/pkg/urpc" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -89,11 +89,11 @@ func (ex *Exec) SetFlags(f *flag.FlagSet) { f.Var(&ex.caps, "cap", "add a capability to the bounding set for the process") f.BoolVar(&ex.detach, "detach", false, "detach from the container's process") f.StringVar(&ex.processPath, "process", "", "path to the process.json") - f.StringVar(&ex.pidFile, "pid-file", "", "filename that the sandbox pid will be written to") + f.StringVar(&ex.pidFile, "pid-file", "", "filename that the container pid will be written to") } // Execute implements subcommands.Command.Execute. It starts a process in an -// already created sandbox. +// already created container. func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { e, id, err := ex.parseArgs(f) if err != nil { @@ -102,17 +102,17 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) conf := args[0].(*boot.Config) waitStatus := args[1].(*syscall.WaitStatus) - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { Fatalf("error loading sandox: %v", err) } if e.WorkingDirectory == "" { - e.WorkingDirectory = s.Spec.Process.Cwd + e.WorkingDirectory = c.Spec.Process.Cwd } if e.Envv == nil { - e.Envv, err = resolveEnvs(s.Spec.Process.Env, ex.env) + e.Envv, err = resolveEnvs(c.Spec.Process.Env, ex.env) if err != nil { Fatalf("error getting environment variables: %v", err) } @@ -136,15 +136,15 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) // inspect the environment PATH which is relative to the root path. // If the user is overriding environment variables, PATH may have been // overwritten. - rootPath := s.Spec.Root.Path + rootPath := c.Spec.Root.Path e.Filename, err = specutils.GetExecutablePath(e.Argv[0], rootPath, e.Envv) if err != nil { Fatalf("error getting executable path: %v", err) } - ws, err := s.Execute(e) + ws, err := c.Execute(e) if err != nil { - Fatalf("error getting processes for sandbox: %v", err) + Fatalf("error getting processes for container: %v", err) } *waitStatus = ws return subcommands.ExitSuccess @@ -196,7 +196,7 @@ func (ex *Exec) execAndWait(waitStatus *syscall.WaitStatus) subcommands.ExitStat // parseArgs parses exec information from the command line or a JSON file // depending on whether the --process flag was used. Returns an ExecArgs and -// the ID of the sandbox to be used. +// the ID of the container to be used. func (ex *Exec) parseArgs(f *flag.FlagSet) (*control.ExecArgs, string, error) { if ex.processPath == "" { // Requires at least a container ID and command. diff --git a/runsc/cmd/kill.go b/runsc/cmd/kill.go index f89e0077e..97a505fac 100644 --- a/runsc/cmd/kill.go +++ b/runsc/cmd/kill.go @@ -25,7 +25,7 @@ import ( "github.com/google/subcommands" "golang.org/x/sys/unix" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" ) // Kill implements subcommands.Command for the "kill" command. @@ -38,7 +38,7 @@ func (*Kill) Name() string { // Synopsis implements subcommands.Command.Synopsis. func (*Kill) Synopsis() string { - return "sends a signal to the sandbox" + return "sends a signal to the container" } // Usage implements subcommands.Command.Usage. @@ -64,9 +64,9 @@ func (*Kill) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) su id := f.Arg(0) conf := args[0].(*boot.Config) - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandbox: %v", err) + Fatalf("error loading container: %v", err) } // The OCI command-line spec says that the signal should be specified @@ -81,7 +81,7 @@ func (*Kill) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) su if err != nil { Fatalf("%v", err) } - if err := s.Signal(sig); err != nil { + if err := c.Signal(sig); err != nil { Fatalf("%v", err) } return subcommands.ExitSuccess diff --git a/runsc/cmd/list.go b/runsc/cmd/list.go index bf7cb41bb..d554bf7cf 100644 --- a/runsc/cmd/list.go +++ b/runsc/cmd/list.go @@ -26,7 +26,7 @@ import ( "github.com/google/subcommands" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" ) // List implements subcommands.Command for the "list" command for the "list" command. @@ -64,7 +64,7 @@ func (l *List) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } conf := args[0].(*boot.Config) - ids, err := sandbox.List(conf.RootDir) + ids, err := container.List(conf.RootDir) if err != nil { Fatalf("%v", err) } @@ -76,14 +76,14 @@ func (l *List) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) return subcommands.ExitSuccess } - // Collect the sandboxes. - var sandboxes []*sandbox.Sandbox + // Collect the containers. + var containers []*container.Container for _, id := range ids { - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandbox %q: %v", id, err) + Fatalf("error loading container %q: %v", id, err) } - sandboxes = append(sandboxes, s) + containers = append(containers, c) } switch l.format { @@ -91,24 +91,24 @@ func (l *List) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) // Print a nice table. w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0) fmt.Fprint(w, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\tOWNER\n") - for _, s := range sandboxes { + for _, c := range containers { fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\n", - s.ID, - s.Pid, - s.Status, - s.BundleDir, - s.CreatedAt.Format(time.RFC3339Nano), - s.Owner) + c.ID, + c.Pid(), + c.Status, + c.BundleDir, + c.CreatedAt.Format(time.RFC3339Nano), + c.Owner) } w.Flush() case "json": // Print just the states. var states []specs.State - for _, s := range sandboxes { - states = append(states, s.State()) + for _, c := range containers { + states = append(states, c.State()) } if err := json.NewEncoder(os.Stdout).Encode(states); err != nil { - Fatalf("error marshaling sandbox state: %v", err) + Fatalf("error marshaling container state: %v", err) } default: Fatalf("unknown list format %q", l.format) diff --git a/runsc/cmd/ps.go b/runsc/cmd/ps.go index a667ec04c..9f9f4d15e 100644 --- a/runsc/cmd/ps.go +++ b/runsc/cmd/ps.go @@ -22,7 +22,7 @@ import ( "github.com/google/subcommands" "gvisor.googlesource.com/gvisor/pkg/sentry/control" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" ) // PS implements subcommands.Command for the "ps" command. @@ -60,13 +60,13 @@ func (ps *PS) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) id := f.Arg(0) conf := args[0].(*boot.Config) - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { Fatalf("error loading sandox: %v", err) } - pList, err := s.Processes() + pList, err := c.Processes() if err != nil { - Fatalf("error getting processes for sandbox: %v", err) + Fatalf("error getting processes for container: %v", err) } switch ps.format { diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index a61a6c73e..681112f30 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -21,7 +21,7 @@ import ( "flag" "github.com/google/subcommands" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -72,9 +72,9 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s Fatalf("error reading spec: %v", err) } - ws, err := sandbox.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile, commandLineFlags()) + ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile) if err != nil { - Fatalf("error running sandbox: %v", err) + Fatalf("error running container: %v", err) } *waitStatus = ws diff --git a/runsc/cmd/start.go b/runsc/cmd/start.go index a8e132497..97ea91fff 100644 --- a/runsc/cmd/start.go +++ b/runsc/cmd/start.go @@ -19,7 +19,7 @@ import ( "flag" "github.com/google/subcommands" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" ) // Start implements subcommands.Command for the "start" command. @@ -53,12 +53,12 @@ func (*Start) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s id := f.Arg(0) conf := args[0].(*boot.Config) - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandbox: %v", err) + Fatalf("error loading container: %v", err) } - if err := s.Start(conf); err != nil { - Fatalf("error starting sandbox: %v", err) + if err := c.Start(conf); err != nil { + Fatalf("error starting container: %v", err) } return subcommands.ExitSuccess } diff --git a/runsc/cmd/state.go b/runsc/cmd/state.go index 0b47f290a..28752d95e 100644 --- a/runsc/cmd/state.go +++ b/runsc/cmd/state.go @@ -23,7 +23,7 @@ import ( "github.com/google/subcommands" "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/container" ) // State implements subcommands.Command for the "state" command. @@ -36,12 +36,12 @@ func (*State) Name() string { // Synopsis implements subcommands.Command.Synopsis. func (*State) Synopsis() string { - return "get the state of a sandbox" + return "get the state of a container" } // Usage implements subcommands.Command.Usage. func (*State) Usage() string { - return `state [flags] - get the state of a sandbox` + return `state [flags] - get the state of a container` } // SetFlags implements subcommands.Command.SetFlags. @@ -57,16 +57,16 @@ func (*State) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s id := f.Arg(0) conf := args[0].(*boot.Config) - s, err := sandbox.Load(conf.RootDir, id) + c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandbox: %v", err) + Fatalf("error loading container: %v", err) } - log.Debugf("Returning state %+v", s) + log.Debugf("Returning state for container %+v", c) // Write json-encoded state directly to stdout. - b, err := json.MarshalIndent(s.State(), "", " ") + b, err := json.MarshalIndent(c.State(), "", " ") if err != nil { - Fatalf("error marshaling sandbox state: %v", err) + Fatalf("error marshaling container state: %v", err) } os.Stdout.Write(b) return subcommands.ExitSuccess diff --git a/runsc/container/BUILD b/runsc/container/BUILD new file mode 100644 index 000000000..c558b4b0a --- /dev/null +++ b/runsc/container/BUILD @@ -0,0 +1,45 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "container", + srcs = [ + "container.go", + "hook.go", + "status.go", + ], + importpath = "gvisor.googlesource.com/gvisor/runsc/container", + visibility = [ + "//runsc:__subpackages__", + ], + deps = [ + "//pkg/log", + "//pkg/sentry/control", + "//runsc/boot", + "//runsc/sandbox", + "//runsc/specutils", + "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + ], +) + +go_test( + name = "container_test", + size = "small", + srcs = ["container_test.go"], + pure = "on", + rundir = ".", + deps = [ + "//pkg/abi/linux", + "//pkg/log", + "//pkg/sentry/control", + "//pkg/sentry/kernel/auth", + "//pkg/unet", + "//runsc/boot", + "//runsc/cmd", + "//runsc/container", + "@com_github_google_subcommands//:go_default_library", + "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@org_golang_x_sys//unix:go_default_library", + ], +) diff --git a/runsc/container/container.go b/runsc/container/container.go new file mode 100644 index 000000000..97115cd6b --- /dev/null +++ b/runsc/container/container.go @@ -0,0 +1,380 @@ +// Copyright 2018 Google Inc. +// +// 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 container creates and manipulates containers. +package container + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strconv" + "syscall" + "time" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/control" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/sandbox" + "gvisor.googlesource.com/gvisor/runsc/specutils" +) + +// metadataFilename is the name of the metadata file relative to the container +// root directory that holds sandbox metadata. +const metadataFilename = "meta.json" + +// validateID validates the container id. +func validateID(id string) error { + // See libcontainer/factory_linux.go. + idRegex := regexp.MustCompile(`^[\w+-\.]+$`) + if !idRegex.MatchString(id) { + return fmt.Errorf("invalid container id: %v", id) + } + return nil +} + +// Container represents a containerized application. When running, the +// container is associated with a single Sandbox. +// +// Container metadata can be saved and loaded to disk. Within a root directory, +// we maintain subdirectories for each container named with the container id. +// The container metadata is is stored as json within the container directory +// in a file named "meta.json". This metadata format is defined by us, and is +// not part of the OCI spec. +// +// Containers must write their metadata file after any change to their internal +// state. The entire container directory is deleted when the container is +// destroyed. +type Container struct { + // ID is the container ID. + ID string `json:"id"` + + // Spec is the OCI runtime spec that configures this container. + Spec *specs.Spec `json:"spec"` + + // BundleDir is the directory containing the container bundle. + BundleDir string `json:"bundleDir"` + + // Root is the directory containing the container metadata file. + Root string `json:"root"` + + // CreatedAt is the time the container was created. + CreatedAt time.Time `json:"createdAt"` + + // Owner is the container owner. + Owner string `json:"owner"` + + // ConsoleSocket is the path to a unix domain socket that will receive + // the console FD. It is only used during create, so we don't need to + // store it in the metadata. + ConsoleSocket string `json:"-"` + + // Status is the current container Status. + Status Status `json:"status"` + + // Sandbox is the sandbox this container is running in. It will be nil + // if the container is not in state Running or Created. + Sandbox *sandbox.Sandbox `json:"sandbox"` +} + +// Load loads a container with the given id from a metadata file. +func Load(rootDir, id string) (*Container, error) { + log.Debugf("Load container %q %q", rootDir, id) + if err := validateID(id); err != nil { + return nil, err + } + cRoot := filepath.Join(rootDir, id) + if !exists(cRoot) { + return nil, fmt.Errorf("container with id %q does not exist", id) + } + metaFile := filepath.Join(cRoot, metadataFilename) + if !exists(metaFile) { + return nil, fmt.Errorf("container with id %q does not have metadata file %q", id, metaFile) + } + metaBytes, err := ioutil.ReadFile(metaFile) + if err != nil { + return nil, fmt.Errorf("error reading container metadata file %q: %v", metaFile, err) + } + var c Container + if err := json.Unmarshal(metaBytes, &c); err != nil { + return nil, fmt.Errorf("error unmarshaling container metadata from %q: %v", metaFile, err) + } + + // If the status is "Running" or "Created", check that the sandbox + // process still exists, and set it to Stopped if it does not. + // + // This is inherently racey. + if c.Status == Running || c.Status == Created { + // Send signal 0 to check if container still exists. + if err := c.Signal(0); err != nil { + // Container no longer exists. + c.Status = Stopped + c.Sandbox = nil + } + } + + return &c, nil +} + +// List returns all container ids in the given root directory. +func List(rootDir string) ([]string, error) { + log.Debugf("List containers %q", rootDir) + fs, err := ioutil.ReadDir(rootDir) + if err != nil { + return nil, fmt.Errorf("ReadDir(%s) failed: %v", rootDir, err) + } + var out []string + for _, f := range fs { + out = append(out, f.Name()) + } + return out, nil +} + +// Create creates the container in a new Sandbox process, unless the metadata +// indicates that an existing Sandbox should be used. +func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string) (*Container, error) { + log.Debugf("Create container %q in root dir: %s", id, conf.RootDir) + if err := validateID(id); err != nil { + return nil, err + } + if err := specutils.ValidateSpec(spec); err != nil { + return nil, err + } + + containerRoot := filepath.Join(conf.RootDir, id) + if exists(containerRoot) { + return nil, fmt.Errorf("container with id %q already exists: %q ", id, containerRoot) + } + + c := &Container{ + ID: id, + Spec: spec, + ConsoleSocket: consoleSocket, + BundleDir: bundleDir, + Root: containerRoot, + Status: Creating, + Owner: os.Getenv("USER"), + } + + // TODO: If the metadata annotations indicates that this + // container should be started in another sandbox, we must do so. The + // metadata will indicate the ID of the sandbox, which is the same as + // the ID of the init container in the sandbox. We can look up that + // init container by ID to get the sandbox, then we need to expose a + // way to run a new container in the sandbox. + + // Start a new sandbox for this container. Any errors after this point + // must destroy the container. + s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket) + if err != nil { + c.Destroy() + return nil, err + } + + c.Sandbox = s + c.Status = Created + + // Save the metadata file. + if err := c.save(); err != nil { + c.Destroy() + return nil, err + } + + // Write the pid file. Containerd considers the create complete after + // this file is created, so it must be the last thing we do. + if pidFile != "" { + if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(c.Pid())), 0644); err != nil { + s.Destroy() + return nil, fmt.Errorf("error writing pid file: %v", err) + } + } + + return c, nil +} + +// Start starts running the containerized process inside the sandbox. +func (c *Container) Start(conf *boot.Config) error { + log.Debugf("Start container %q", c.ID) + if c.Status != Created { + return fmt.Errorf("cannot start container in state %s", c.Status) + } + + // "If any prestart hook fails, the runtime MUST generate an error, + // stop and destroy the container". + if c.Spec.Hooks != nil { + if err := executeHooks(c.Spec.Hooks.Prestart, c.State()); err != nil { + c.Destroy() + return err + } + } + + if err := c.Sandbox.Start(c.ID, c.Spec, conf); err != nil { + c.Destroy() + return err + } + + // "If any poststart hook fails, the runtime MUST log a warning, but + // the remaining hooks and lifecycle continue as if the hook had + // succeeded". + if c.Spec.Hooks != nil { + executeHooksBestEffort(c.Spec.Hooks.Poststart, c.State()) + } + + c.Status = Running + return c.save() +} + +// Run is a helper that calls Create + Start + Wait. +func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string) (syscall.WaitStatus, error) { + log.Debugf("Run container %q in root dir: %s", id, conf.RootDir) + c, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile) + if err != nil { + return 0, fmt.Errorf("error creating container: %v", err) + } + if err := c.Start(conf); err != nil { + return 0, fmt.Errorf("error starting container: %v", err) + } + return c.Wait() +} + +// Execute runs the specified command in the container. +func (c *Container) Execute(e *control.ExecArgs) (syscall.WaitStatus, error) { + log.Debugf("Execute in container %q, args: %+v", c.ID, e) + if c.Status != Created && c.Status != Running { + return 0, fmt.Errorf("cannot exec in container in state %s", c.Status) + } + return c.Sandbox.Execute(c.ID, e) +} + +// Event returns events for the container. +func (c *Container) Event() (*boot.Event, error) { + log.Debugf("Getting events for container %q", c.ID) + if c.Status != Running && c.Status != Created { + return nil, fmt.Errorf("cannot get events for container in state: %s", c.Status) + } + return c.Sandbox.Event(c.ID) +} + +// Pid returns the Pid of the sandbox the container is running in, or -1 if the +// container is not running. +func (c *Container) Pid() int { + if c.Status != Running && c.Status != Created { + return -1 + } + return c.Sandbox.Pid +} + +// Wait waits for the container to exit, and returns its WaitStatus. +func (c *Container) Wait() (syscall.WaitStatus, error) { + log.Debugf("Wait on container %q", c.ID) + return c.Sandbox.Wait(c.ID) +} + +// Signal sends the signal to the container. +func (c *Container) Signal(sig syscall.Signal) error { + log.Debugf("Signal container %q", c.ID) + if c.Status == Stopped { + log.Warningf("container %q not running, not sending signal %v", c.ID, sig) + return nil + } + return c.Sandbox.Signal(c.ID, sig) +} + +// State returns the metadata of the container. +func (c *Container) State() specs.State { + return specs.State{ + Version: specs.Version, + ID: c.ID, + Status: c.Status.String(), + Pid: c.Pid(), + Bundle: c.BundleDir, + } +} + +// Processes retrieves the list of processes and associated metadata inside a +// container. +func (c *Container) Processes() ([]*control.Process, error) { + if c.Status != Running { + return nil, fmt.Errorf("cannot get processes of container %q because it isn't running. It is in state %v", c.ID, c.Status) + } + return c.Sandbox.Processes(c.ID) +} + +// Destroy frees all resources associated with the container. +func (c *Container) Destroy() error { + log.Debugf("Destroy container %q", c.ID) + + // First stop the container. + if err := c.Sandbox.Stop(c.ID); err != nil { + return err + } + + // Then destroy all the metadata. + if err := os.RemoveAll(c.Root); err != nil { + log.Warningf("Failed to delete container root directory %q, err: %v", c.Root, err) + } + + // "If any poststop hook fails, the runtime MUST log a warning, but the + // remaining hooks and lifecycle continue as if the hook had succeeded". + if c.Spec.Hooks != nil && (c.Status == Created || c.Status == Running) { + executeHooksBestEffort(c.Spec.Hooks.Poststop, c.State()) + } + + if err := os.RemoveAll(c.Root); err != nil { + log.Warningf("Failed to delete container root directory %q, err: %v", c.Root, err) + } + + // If we are the first container in the sandbox, take the sandbox down + // as well. + if c.Sandbox != nil && c.Sandbox.ID == c.ID { + if err := c.Sandbox.Destroy(); err != nil { + log.Warningf("Failed to destroy sandbox %q: %v", c.Sandbox.ID, err) + } + } + + c.Sandbox = nil + c.Status = Stopped + return nil +} + +// save saves the container metadata to a file. +func (c *Container) save() error { + log.Debugf("Save container %q", c.ID) + if err := os.MkdirAll(c.Root, 0711); err != nil { + return fmt.Errorf("error creating container root directory %q: %v", c.Root, err) + } + meta, err := json.Marshal(c) + if err != nil { + return fmt.Errorf("error marshaling container metadata: %v", err) + } + metaFile := filepath.Join(c.Root, metadataFilename) + if err := ioutil.WriteFile(metaFile, meta, 0640); err != nil { + return fmt.Errorf("error writing container metadata: %v", err) + } + return nil +} + +// exists returns true if the given file exists. +func exists(f string) bool { + if _, err := os.Stat(f); err == nil { + return true + } else if !os.IsNotExist(err) { + log.Warningf("error checking for file %q: %v", f, err) + } + return false +} diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go new file mode 100644 index 000000000..67efd2f9e --- /dev/null +++ b/runsc/container/container_test.go @@ -0,0 +1,669 @@ +// Copyright 2018 Google Inc. +// +// 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 container_test + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "os/signal" + "path/filepath" + "reflect" + "strings" + "syscall" + "testing" + "time" + + "context" + "flag" + "github.com/google/subcommands" + specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/control" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" + "gvisor.googlesource.com/gvisor/pkg/unet" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/cmd" + "gvisor.googlesource.com/gvisor/runsc/container" +) + +func init() { + log.SetLevel(log.Debug) +} + +// writeSpec writes the spec to disk in the given directory. +func writeSpec(dir string, spec *specs.Spec) error { + b, err := json.Marshal(spec) + if err != nil { + return err + } + return ioutil.WriteFile(filepath.Join(dir, "config.json"), b, 0755) +} + +// newSpecWithArgs creates a simple spec with the given args suitable for use +// in tests. +func newSpecWithArgs(args ...string) *specs.Spec { + spec := &specs.Spec{ + // The host filesystem root is the container root. + Root: &specs.Root{ + Path: "/", + Readonly: true, + }, + Process: &specs.Process{ + Args: args, + Env: []string{ + "PATH=" + os.Getenv("PATH"), + }, + }, + } + return spec +} + +// shutdownSignal will be sent to the sandbox in order to shut down cleanly. +const shutdownSignal = syscall.SIGUSR2 + +// setupContainer creates a bundle and root dir for the container, generates a +// test config, and writes the spec to config.json in the bundle dir. +func setupContainer(spec *specs.Spec) (rootDir, bundleDir string, conf *boot.Config, err error) { + rootDir, err = ioutil.TempDir("", "containers") + if err != nil { + return "", "", nil, fmt.Errorf("error creating root dir: %v", err) + } + + bundleDir, err = ioutil.TempDir("", "bundle") + if err != nil { + return "", "", nil, fmt.Errorf("error creating bundle dir: %v", err) + } + + if err = writeSpec(bundleDir, spec); err != nil { + return "", "", nil, fmt.Errorf("error writing spec: %v", err) + } + + conf = &boot.Config{ + RootDir: rootDir, + Network: boot.NetworkNone, + // Don't add flags when calling subprocesses, since the test + // runner does not know about all the flags. We control the + // Config in the subprocess anyways, so it does not matter. + TestModeNoFlags: true, + } + + return rootDir, bundleDir, conf, nil +} + +// uniqueContainerID generates a unique container id for each test. +// +// The container id is used to create an abstract unix domain socket, which must +// be unique. While the container forbids creating two containers with the same +// name, sometimes between test runs the socket does not get cleaned up quickly +// enough, causing container creation to fail. +func uniqueContainerID() string { + return fmt.Sprintf("test-container-%d", time.Now().UnixNano()) +} + +// waitForProcessList waits for the given process list to show up in the container. +func waitForProcessList(s *container.Container, expected []*control.Process) error { + var got []*control.Process + for start := time.Now(); time.Now().Sub(start) < 10*time.Second; { + var err error + got, err := s.Processes() + if err != nil { + return fmt.Errorf("error getting process data from container: %v", err) + } + if procListsEqual(got, expected) { + return nil + } + // Process might not have started, try again... + time.Sleep(10 * time.Millisecond) + } + return fmt.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(expected)) +} + +// TestLifecycle tests the basic Create/Start/Signal/Destroy container lifecycle. +// It verifies after each step that the container can be loaded from disk, and +// has the correct status. +func TestLifecycle(t *testing.T) { + // The container will just sleep for a long time. We will kill it before + // it finishes sleeping. + spec := newSpecWithArgs("sleep", "100") + + rootDir, bundleDir, conf, err := setupContainer(spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + // expectedPL lists the expected process state of the container. + expectedPL := []*control.Process{ + { + UID: 0, + PID: 1, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + } + // Create the container. + id := uniqueContainerID() + if _, err := container.Create(id, spec, conf, bundleDir, "", ""); err != nil { + t.Fatalf("error creating container: %v", err) + } + // Load the container from disk and check the status. + s, err := container.Load(rootDir, id) + if err != nil { + t.Fatalf("error loading container: %v", err) + } + if got, want := s.Status, container.Created; got != want { + t.Errorf("container status got %v, want %v", got, want) + } + + // List should return the container id. + ids, err := container.List(rootDir) + if err != nil { + t.Fatalf("error listing containers: %v", err) + } + if got, want := ids, []string{id}; !reflect.DeepEqual(got, want) { + t.Errorf("container list got %v, want %v", got, want) + } + + // Start the container. + if err := s.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + // Load the container from disk and check the status. + s, err = container.Load(rootDir, id) + if err != nil { + t.Fatalf("error loading container: %v", err) + } + if got, want := s.Status, container.Running; got != want { + t.Errorf("container status got %v, want %v", got, want) + } + + // Verify that "sleep 100" is running. + if err := waitForProcessList(s, expectedPL); err != nil { + t.Error(err) + } + + // Send the container a signal, which we catch and use to cleanly + // shutdown. + if err := s.Signal(shutdownSignal); err != nil { + t.Fatalf("error sending signal %v to container: %v", shutdownSignal, err) + } + // Wait for it to die. + if _, err := s.Wait(); err != nil { + t.Fatalf("error waiting on container: %v", err) + } + // Load the container from disk and check the status. + s, err = container.Load(rootDir, id) + if err != nil { + t.Fatalf("error loading container: %v", err) + } + if got, want := s.Status, container.Stopped; got != want { + t.Errorf("container status got %v, want %v", got, want) + } + + // Destroy the container. + if err := s.Destroy(); err != nil { + t.Fatalf("error destroying container: %v", err) + } + + // List should not return the container id. + ids, err = container.List(rootDir) + if err != nil { + t.Fatalf("error listing containers: %v", err) + } + if len(ids) != 0 { + t.Errorf("expected container list to be empty, but got %v", ids) + } + + // Loading the container by id should fail. + if _, err = container.Load(rootDir, id); err == nil { + t.Errorf("expected loading destroyed container to fail, but it did not") + } +} + +// Test the we can execute the application with different path formats. +func TestExePath(t *testing.T) { + for _, test := range []struct { + path string + success bool + }{ + {path: "true", success: true}, + {path: "bin/true", success: true}, + {path: "/bin/true", success: true}, + {path: "thisfiledoesntexit", success: false}, + {path: "bin/thisfiledoesntexit", success: false}, + {path: "/bin/thisfiledoesntexit", success: false}, + } { + spec := newSpecWithArgs(test.path) + rootDir, bundleDir, conf, err := setupContainer(spec) + if err != nil { + t.Fatalf("exec: %s, error setting up container: %v", test.path, err) + } + + ws, err := container.Run(uniqueContainerID(), spec, conf, bundleDir, "", "") + + os.RemoveAll(rootDir) + os.RemoveAll(bundleDir) + + if test.success { + if err != nil { + t.Errorf("exec: %s, error running container: %v", test.path, err) + } + if ws.ExitStatus() != 0 { + t.Errorf("exec: %s, got exit status %v want %v", test.path, ws.ExitStatus(), 0) + } + } else { + if err == nil { + t.Errorf("exec: %s, got: no error, want: error", test.path) + } + } + } +} + +// Test the we can retrieve the application exit status from the container. +func TestAppExitStatus(t *testing.T) { + // First container will succeed. + succSpec := newSpecWithArgs("true") + + rootDir, bundleDir, conf, err := setupContainer(succSpec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + ws, err := container.Run(uniqueContainerID(), succSpec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error running container: %v", err) + } + if ws.ExitStatus() != 0 { + t.Errorf("got exit status %v want %v", ws.ExitStatus(), 0) + } + + // Second container exits with non-zero status. + wantStatus := 123 + errSpec := newSpecWithArgs("bash", "-c", fmt.Sprintf("exit %d", wantStatus)) + + rootDir2, bundleDir2, conf, err := setupContainer(errSpec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir2) + defer os.RemoveAll(bundleDir2) + + ws, err = container.Run(uniqueContainerID(), succSpec, conf, bundleDir2, "", "") + if err != nil { + t.Fatalf("error running container: %v", err) + } + if ws.ExitStatus() != wantStatus { + t.Errorf("got exit status %v want %v", ws.ExitStatus(), wantStatus) + } +} + +// TestExec verifies that a container can exec a new program. +func TestExec(t *testing.T) { + const uid = 343 + spec := newSpecWithArgs("sleep", "100") + + rootDir, bundleDir, conf, err := setupContainer(spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + // Create and start the container. + s, err := container.Create(uniqueContainerID(), spec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer s.Destroy() + if err := s.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + + // expectedPL lists the expected process state of the container. + expectedPL := []*control.Process{ + { + UID: 0, + PID: 1, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + { + UID: uid, + PID: 2, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + } + + // Verify that "sleep 100" is running. + if err := waitForProcessList(s, expectedPL[:1]); err != nil { + t.Error(err) + } + + execArgs := control.ExecArgs{ + Filename: "/bin/sleep", + Argv: []string{"sleep", "5"}, + Envv: []string{"PATH=" + os.Getenv("PATH")}, + WorkingDirectory: "/", + KUID: uid, + } + + // Verify that "sleep 100" and "sleep 5" are running after exec. + // First, start running exec (whick blocks). + status := make(chan error, 1) + go func() { + exitStatus, err := s.Execute(&execArgs) + if err != nil { + status <- err + } else if exitStatus != 0 { + status <- fmt.Errorf("failed with exit status: %v", exitStatus) + } else { + status <- nil + } + }() + + if err := waitForProcessList(s, expectedPL); err != nil { + t.Fatal(err) + } + + // Ensure that exec finished without error. + select { + case <-time.After(10 * time.Second): + t.Fatalf("container timed out waiting for exec to finish.") + case st := <-status: + if st != nil { + t.Errorf("container failed to exec %v: %v", execArgs, err) + } + } +} + +// TestCapabilities verifies that: +// - Running exec as non-root UID and GID will result in an error (because the +// executable file can't be read). +// - Running exec as non-root with CAP_DAC_OVERRIDE succeeds because it skips +// this check. +func TestCapabilities(t *testing.T) { + const uid = 343 + const gid = 2401 + spec := newSpecWithArgs("sleep", "100") + + // We generate files in the host temporary directory. + spec.Mounts = append(spec.Mounts, specs.Mount{ + Destination: os.TempDir(), + Source: os.TempDir(), + Type: "bind", + }) + + rootDir, bundleDir, conf, err := setupContainer(spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + // Create and start the container. + s, err := container.Create(uniqueContainerID(), spec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer s.Destroy() + if err := s.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + + // expectedPL lists the expected process state of the container. + expectedPL := []*control.Process{ + { + UID: 0, + PID: 1, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + { + UID: uid, + PID: 2, + PPID: 0, + C: 0, + Cmd: "exe", + }, + } + if err := waitForProcessList(s, expectedPL[:1]); err != nil { + t.Fatalf("Failed to wait for sleep to start, err: %v", err) + } + + // Create an executable that can't be run with the specified UID:GID. + // This shouldn't be callable within the container until we add the + // CAP_DAC_OVERRIDE capability to skip the access check. + exePath := filepath.Join(rootDir, "exe") + if err := ioutil.WriteFile(exePath, []byte("#!/bin/sh\necho hello"), 0770); err != nil { + t.Fatalf("couldn't create executable: %v", err) + } + defer os.Remove(exePath) + + // Need to traverse the intermediate directory. + os.Chmod(rootDir, 0755) + + execArgs := control.ExecArgs{ + Filename: exePath, + Argv: []string{exePath}, + Envv: []string{"PATH=" + os.Getenv("PATH")}, + WorkingDirectory: "/", + KUID: uid, + KGID: gid, + Capabilities: &auth.TaskCapabilities{}, + } + + // "exe" should fail because we don't have the necessary permissions. + if _, err := s.Execute(&execArgs); err == nil { + t.Fatalf("container executed without error, but an error was expected") + } + + // Now we run with the capability enabled and should succeed. + execArgs.Capabilities = &auth.TaskCapabilities{ + EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), + } + // "exe" should not fail this time. + if _, err := s.Execute(&execArgs); err != nil { + t.Fatalf("container failed to exec %v: %v", execArgs, err) + } +} + +// Test that an tty FD is sent over the console socket if one is provided. +func TestConsoleSocket(t *testing.T) { + spec := newSpecWithArgs("true") + rootDir, bundleDir, conf, err := setupContainer(spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + // Create a named socket and start listening. We use a relative path + // to avoid overflowing the unix path length limit (108 chars). + socketPath := filepath.Join(bundleDir, "socket") + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("error getting cwd: %v", err) + } + socketRelPath, err := filepath.Rel(cwd, socketPath) + if err != nil { + t.Fatalf("error getting relative path for %q from cwd %q: %v", socketPath, cwd, err) + } + if len(socketRelPath) > len(socketPath) { + socketRelPath = socketPath + } + srv, err := unet.BindAndListen(socketRelPath, false) + if err != nil { + t.Fatalf("error binding and listening to socket %q: %v", socketPath, err) + } + defer os.Remove(socketPath) + + // Create the container and pass the socket name. + id := uniqueContainerID() + s, err := container.Create(id, spec, conf, bundleDir, socketRelPath, "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + + // Open the othe end of the socket. + sock, err := srv.Accept() + if err != nil { + t.Fatalf("error accepting socket connection: %v", err) + } + + // Allow 3 fds to be received. We only expect 1. + r := sock.Reader(true /* blocking */) + r.EnableFDs(1) + + // The socket is closed right after sending the FD, so EOF is + // an allowed error. + b := [][]byte{{}} + if _, err := r.ReadVec(b); err != nil && err != io.EOF { + t.Fatalf("error reading from socket connection: %v", err) + } + + // We should have gotten a control message. + fds, err := r.ExtractFDs() + if err != nil { + t.Fatalf("error extracting fds from socket connection: %v", err) + } + if len(fds) != 1 { + t.Fatalf("got %d fds from socket, wanted 1", len(fds)) + } + + // Verify that the fd is a terminal. + if _, err := unix.IoctlGetTermios(fds[0], unix.TCGETS); err != nil { + t.Errorf("fd is not a terminal (ioctl TGGETS got %v)", err) + } + + // Shut it down. + if err := s.Destroy(); err != nil { + t.Fatalf("error destroying container: %v", err) + } + + // Close socket. + if err := srv.Close(); err != nil { + t.Fatalf("error destroying container: %v", err) + } +} + +func TestSpecUnsupported(t *testing.T) { + spec := newSpecWithArgs("/bin/true") + spec.Process.SelinuxLabel = "somelabel" + + // These are normally set by docker and will just cause warnings to be logged. + spec.Process.ApparmorProfile = "someprofile" + spec.Linux = &specs.Linux{Seccomp: &specs.LinuxSeccomp{}} + + rootDir, bundleDir, conf, err := setupContainer(spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + id := uniqueContainerID() + _, err = container.Create(id, spec, conf, bundleDir, "", "") + if err == nil || !strings.Contains(err.Error(), "is not supported") { + t.Errorf("container.Create() wrong error, got: %v, want: *is not supported, spec.Process: %+v", err, spec.Process) + } +} + +// procListsEqual is used to check whether 2 Process lists are equal for all +// implemented fields. +func procListsEqual(got, want []*control.Process) bool { + if len(got) != len(want) { + return false + } + for i := range got { + pd1 := got[i] + pd2 := want[i] + // Zero out unimplemented and timing dependant fields. + pd1.Time, pd2.Time = "", "" + pd1.STime, pd2.STime = "", "" + pd1.C, pd2.C = 0, 0 + if *pd1 != *pd2 { + return false + } + } + return true +} + +func procListToString(pl []*control.Process) string { + strs := make([]string, 0, len(pl)) + for _, p := range pl { + strs = append(strs, fmt.Sprintf("%+v", p)) + } + return fmt.Sprintf("[%s]", strings.Join(strs, ",")) +} + +// TestMain acts like runsc if it is called with the "boot" argument, otherwise +// it just runs the tests. This is required because creating a container will +// call "/proc/self/exe boot". Normally /proc/self/exe is the runsc binary, +// but for tests we have to fake it. +func TestMain(m *testing.M) { + // exit writes coverage data before exiting. + exit := func(status int) { + os.Exit(status) + } + + if !flag.Parsed() { + flag.Parse() + } + + // If we are passed one of the commands then run it. + subcommands.Register(new(cmd.Boot), "boot") + subcommands.Register(new(cmd.Gofer), "gofer") + switch flag.Arg(0) { + case "boot", "gofer": + // Run the command in a goroutine so we can block the main + // thread waiting for shutdownSignal. + go func() { + conf := &boot.Config{ + RootDir: "unused-root-dir", + Network: boot.NetworkNone, + } + var ws syscall.WaitStatus + subcmdCode := subcommands.Execute(context.Background(), conf, &ws) + if subcmdCode != subcommands.ExitSuccess { + panic(fmt.Sprintf("command failed to execute, err: %v", subcmdCode)) + } + // Container exited normally. Shut down this process. + os.Exit(ws.ExitStatus()) + }() + + // Shutdown cleanly when the shutdownSignal is received. This + // allows us to write coverage data before exiting. + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, shutdownSignal) + <-sigc + exit(0) + default: + // Otherwise run the tests. + exit(m.Run()) + } +} diff --git a/runsc/container/hook.go b/runsc/container/hook.go new file mode 100644 index 000000000..3d93ca0be --- /dev/null +++ b/runsc/container/hook.go @@ -0,0 +1,111 @@ +// Copyright 2018 Google Inc. +// +// 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 container + +import ( + "bytes" + "encoding/json" + "fmt" + "os/exec" + "path/filepath" + "strings" + "time" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.googlesource.com/gvisor/pkg/log" +) + +// This file implements hooks as defined in OCI spec: +// https://github.com/opencontainers/runtime-spec/blob/master/config.md#toc22 +// +// "hooks":{ +// "prestart":[{ +// "path":"/usr/bin/dockerd", +// "args":[ +// "libnetwork-setkey", "arg2", +// ] +// }] +// }, + +// executeHooksBestEffort executes hooks and logs warning in case they fail. +// Runs all hooks, always. +func executeHooksBestEffort(hooks []specs.Hook, s specs.State) { + for _, h := range hooks { + if err := executeHook(h, s); err != nil { + log.Warningf("Failure to execute hook %+v, err: %v", h, err) + } + } +} + +// executeHooks executes hooks until the first one fails or they all execute. +func executeHooks(hooks []specs.Hook, s specs.State) error { + for _, h := range hooks { + if err := executeHook(h, s); err != nil { + return err + } + } + return nil +} + +func executeHook(h specs.Hook, s specs.State) error { + log.Debugf("Executing hook %+v, state: %+v", h, s) + + if strings.TrimSpace(h.Path) == "" { + return fmt.Errorf("empty path for hook") + } + if !filepath.IsAbs(h.Path) { + return fmt.Errorf("path for hook is not absolute: %q", h.Path) + } + + b, err := json.Marshal(s) + if err != nil { + return err + } + var stdout, stderr bytes.Buffer + cmd := exec.Cmd{ + Path: h.Path, + Args: h.Args, + Env: h.Env, + Stdin: bytes.NewReader(b), + Stdout: &stdout, + Stderr: &stderr, + } + if err := cmd.Start(); err != nil { + return err + } + + c := make(chan error, 1) + go func() { + c <- cmd.Wait() + }() + + var timer <-chan time.Time + if h.Timeout != nil { + timer = time.After(time.Duration(*h.Timeout) * time.Second) + } + select { + case err := <-c: + if err != nil { + return fmt.Errorf("failure executing hook %q, err: %v\nstdout: %s\nstderr: %s", h.Path, err, stdout.String(), stderr.String()) + } + case <-timer: + cmd.Process.Kill() + cmd.Wait() + return fmt.Errorf("timeout executing hook %q\nstdout: %s\nstderr: %s", h.Path, stdout.String(), stderr.String()) + } + + log.Debugf("Execute hook %q success!", h.Path) + return nil +} diff --git a/runsc/container/status.go b/runsc/container/status.go new file mode 100644 index 000000000..8da1b4e89 --- /dev/null +++ b/runsc/container/status.go @@ -0,0 +1,54 @@ +// Copyright 2018 Google Inc. +// +// 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 container + +// Status enumerates container statuses. The statuses and their semantics are +// part of the runtime CLI spec. +type Status int + +const ( + // Creating indicates "the container is being created". + Creating Status = iota + + // Created indicates "the runtime has finished the create operation and + // the container process has neither exited nor executed the + // user-specified program". + Created + + // Running indicates "the container process has executed the + // user-specified program but has not exited". + Running + + // Stopped indicates "the container process has exited". + Stopped +) + +// String converts a Status to a string. These strings are part of the runtime +// CLI spec and should not be changed. +func (s Status) String() string { + switch s { + case Creating: + return "creating" + case Created: + return "created" + case Running: + return "running" + case Stopped: + return "stopped" + default: + return "unknown" + } + +} diff --git a/runsc/main.go b/runsc/main.go index 3311514d2..42c8ee315 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -109,6 +109,10 @@ func main() { // Create a new Config from the flags. conf := &boot.Config{ RootDir: *rootDir, + Debug: *debug, + LogFilename: *logFilename, + LogFormat: *logFormat, + DebugLogDir: *debugLogDir, FileAccess: fsAccess, Overlay: *overlay, Network: netType, diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index bdd95903e..e89b19552 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -1,16 +1,14 @@ package(licenses = ["notice"]) # Apache 2.0 -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "sandbox", srcs = [ "console.go", - "hook.go", "namespace.go", "network.go", "sandbox.go", - "status.go", ], importpath = "gvisor.googlesource.com/gvisor/runsc/sandbox", visibility = [ @@ -30,24 +28,3 @@ go_library( "@org_golang_x_sys//unix:go_default_library", ], ) - -go_test( - name = "sandbox_test", - size = "small", - srcs = ["sandbox_test.go"], - pure = "on", - rundir = ".", - deps = [ - "//pkg/abi/linux", - "//pkg/log", - "//pkg/sentry/control", - "//pkg/sentry/kernel/auth", - "//pkg/unet", - "//runsc/boot", - "//runsc/cmd", - "//runsc/sandbox", - "@com_github_google_subcommands//:go_default_library", - "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", - "@org_golang_x_sys//unix:go_default_library", - ], -) diff --git a/runsc/sandbox/hook.go b/runsc/sandbox/hook.go deleted file mode 100644 index 40b064cdc..000000000 --- a/runsc/sandbox/hook.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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 sandbox - -import ( - "bytes" - "encoding/json" - "fmt" - "os/exec" - "path/filepath" - "strings" - "time" - - specs "github.com/opencontainers/runtime-spec/specs-go" - "gvisor.googlesource.com/gvisor/pkg/log" -) - -// This file implements hooks as defined in OCI spec: -// https://github.com/opencontainers/runtime-spec/blob/master/config.md#toc22 -// -// "hooks":{ -// "prestart":[{ -// "path":"/usr/bin/dockerd", -// "args":[ -// "libnetwork-setkey", "arg2", -// ] -// }] -// }, - -// executeHooksBestEffort executes hooks and logs warning in case they fail. -// Runs all hooks, always. -func executeHooksBestEffort(hooks []specs.Hook, s specs.State) { - for _, h := range hooks { - if err := executeHook(h, s); err != nil { - log.Warningf("Failure to execute hook %+v, err: %v", h, err) - } - } -} - -// executeHooks executes hooks until the first one fails or they all execute. -func executeHooks(hooks []specs.Hook, s specs.State) error { - for _, h := range hooks { - if err := executeHook(h, s); err != nil { - return err - } - } - return nil -} - -func executeHook(h specs.Hook, s specs.State) error { - log.Debugf("Executing hook %+v, state: %+v", h, s) - - if strings.TrimSpace(h.Path) == "" { - return fmt.Errorf("empty path for hook") - } - if !filepath.IsAbs(h.Path) { - return fmt.Errorf("path for hook is not absolute: %q", h.Path) - } - - b, err := json.Marshal(s) - if err != nil { - return err - } - var stdout, stderr bytes.Buffer - cmd := exec.Cmd{ - Path: h.Path, - Args: h.Args, - Env: h.Env, - Stdin: bytes.NewReader(b), - Stdout: &stdout, - Stderr: &stderr, - } - if err := cmd.Start(); err != nil { - return err - } - - c := make(chan error, 1) - go func() { - c <- cmd.Wait() - }() - - var timer <-chan time.Time - if h.Timeout != nil { - timer = time.After(time.Duration(*h.Timeout) * time.Second) - } - select { - case err := <-c: - if err != nil { - return fmt.Errorf("failure executing hook %q, err: %v\nstdout: %s\nstderr: %s", h.Path, err, stdout.String(), stderr.String()) - } - case <-timer: - cmd.Process.Kill() - cmd.Wait() - return fmt.Errorf("timeout executing hook %q\nstdout: %s\nstderr: %s", h.Path, stdout.String(), stderr.String()) - } - - log.Debugf("Execute hook %q success!", h.Path) - return nil -} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 34bd6ea67..5dfa4cf0b 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -16,13 +16,9 @@ package sandbox import ( - "encoding/json" "fmt" - "io/ioutil" "os" "os/exec" - "path/filepath" - "regexp" "strconv" "syscall" "time" @@ -38,308 +34,110 @@ import ( "gvisor.googlesource.com/gvisor/runsc/specutils" ) -// metadataFilename is the name of the metadata file relative to sandboxRoot -// that holds sandbox metadata. -const metadataFilename = "meta.json" - -// See libcontainer/factory_linux.go -var idRegex = regexp.MustCompile(`^[\w+-\.]+$`) - -// validateID validates the sandbox id. -func validateID(id string) error { - if !idRegex.MatchString(id) { - return fmt.Errorf("invalid sandbox id: %v", id) - } - return nil -} - -func validateSpec(spec *specs.Spec) error { - if spec.Process.SelinuxLabel != "" { - return fmt.Errorf("SELinux is not supported: %s", spec.Process.SelinuxLabel) - } - - // Docker uses AppArmor by default, so just log that it's being ignored. - if spec.Process.ApparmorProfile != "" { - log.Warningf("AppArmor profile %q is being ignored", spec.Process.ApparmorProfile) - } - // TODO: Apply seccomp to application inside sandbox. - if spec.Linux != nil && spec.Linux.Seccomp != nil { - log.Warningf("Seccomp spec is being ignored") - } - return nil -} - -// Sandbox wraps a child sandbox process, and is responsible for saving and -// loading sandbox metadata to disk. -// -// Within a root directory, we maintain subdirectories for each sandbox named -// with the sandbox id. The sandbox metadata is is stored as json within the -// sandbox directory in a file named "meta.json". This metadata format is -// defined by us, and is not part of the OCI spec. -// -// Sandboxes must write this metadata file after any change to their internal -// state. The entire sandbox directory is deleted when the sandbox is -// destroyed. +// Sandbox wraps a sandbox process. // -// TODO: Protect against concurrent changes to the sandbox metadata -// file. +// It is used to start/stop sandbox process (and associated processes like +// gofers), as well as for running and manipulating containers inside a running +// sandbox. type Sandbox struct { - // ID is the sandbox ID. + // ID is the id of the sandbox. By convention, this is the same ID as + // the first container run in the sandbox. ID string `json:"id"` - // Spec is the OCI runtime spec that configures this sandbox. - Spec *specs.Spec `json:"spec"` - - // BundleDir is the directory containing the sandbox bundle. - BundleDir string `json:"bundleDir"` - - // SandboxRoot is the directory containing the sandbox metadata file. - SandboxRoot string `json:"sandboxRoot"` - - // CreatedAt is the time the sandbox was created. - CreatedAt time.Time `json:"createdAt"` - - // Owner is the sandbox owner. - Owner string `json:"owner"` - - // ConsoleSocket is the path to a unix domain socket that will receive - // the console FD. It is only used during create, so we don't need to - // store it in the metadata. - ConsoleSocket string `json:"-"` - - // Pid is the pid of the running sandbox. Only valid if Status is - // Created or Running. + // Pid is the pid of the running sandbox. May be 0 is the sandbox is + // not running. Pid int `json:"pid"` - // GoferPid is the pid of the gofer running along side the sandbox. May be 0 - // if the gofer has been killed or it's not being used. + // GoferPid is the pid of the gofer running along side the sandbox. May + // be 0 if the gofer has been killed or it's not being used. GoferPid int `json:"goferPid"` - - // Status is the current sandbox Status. - Status Status `json:"status"` } -// Create creates the sandbox subprocess and writes the metadata file. Args -// are additional arguments that will be passed to the sandbox process. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string, args []string) (*Sandbox, error) { - log.Debugf("Create sandbox %q in root dir: %s", id, conf.RootDir) - if err := validateID(id); err != nil { - return nil, err - } - if err := validateSpec(spec); err != nil { - return nil, err - } - - sandboxRoot := filepath.Join(conf.RootDir, id) - if exists(sandboxRoot) { - return nil, fmt.Errorf("sandbox with id %q already exists: %q ", id, sandboxRoot) - } +// Create creates the sandbox process. +func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string) (*Sandbox, error) { + s := &Sandbox{ID: id} - s := &Sandbox{ - ID: id, - Spec: spec, - ConsoleSocket: consoleSocket, - BundleDir: bundleDir, - SandboxRoot: sandboxRoot, - Status: Creating, - Owner: os.Getenv("USER"), - } - - // Create sandbox process. If anything errors between now and the end of this - // function, we MUST clean up all sandbox resources. - if err := s.createProcesses(conf, args); err != nil { - s.Destroy() + binPath, err := specutils.BinPath() + if err != nil { return nil, err } - // Wait for the control server to come up (or timeout). The sandbox is - // not "created" until that happens. - if err := s.waitForCreated(10 * time.Second); err != nil { - s.Destroy() + // Create the gofer process. + ioFiles, err := s.createGoferProcess(spec, conf, bundleDir, binPath) + if err != nil { return nil, err } - s.Status = Created - s.CreatedAt = time.Now() - - // Save the metadata file. - if err := s.save(); err != nil { - s.Destroy() + // Create the sandbox process. + if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, binPath, ioFiles); err != nil { return nil, err } - // Write the pid file. Containerd consideres the create complete after - // this file is created, so it must be the last thing we do. - if pidFile != "" { - if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(s.Pid)), 0644); err != nil { - s.Destroy() - return nil, fmt.Errorf("error writing pid file: %v", err) - } - } - - return s, nil -} - -// Run is a helper that calls Create + Start + Wait. -func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string, args []string) (syscall.WaitStatus, error) { - s, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile, args) - if err != nil { - return 0, fmt.Errorf("error creating sandbox: %v", err) - } - if err := s.Start(conf); err != nil { - return 0, fmt.Errorf("error starting sandbox: %v", err) - } - return s.Wait() -} - -// Load loads a sandbox from with the given id from a metadata file. -func Load(rootDir, id string) (*Sandbox, error) { - log.Debugf("Load sandbox %q %q", rootDir, id) - if err := validateID(id); err != nil { + // Wait for the control server to come up (or timeout). + if err := s.waitForCreated(10 * time.Second); err != nil { return nil, err } - sandboxRoot := filepath.Join(rootDir, id) - if !exists(sandboxRoot) { - return nil, fmt.Errorf("sandbox with id %q does not exist", id) - } - metaFile := filepath.Join(sandboxRoot, metadataFilename) - if !exists(metaFile) { - return nil, fmt.Errorf("sandbox with id %q does not have metadata file %q", id, metaFile) - } - metaBytes, err := ioutil.ReadFile(metaFile) - if err != nil { - return nil, fmt.Errorf("error reading sandbox metadata file %q: %v", metaFile, err) - } - var s Sandbox - if err := json.Unmarshal(metaBytes, &s); err != nil { - return nil, fmt.Errorf("error unmarshaling sandbox metadata from %q: %v", metaFile, err) - } - - // If the status is "Running" or "Created", check that the process - // still exists, and set it to Stopped if it does not. - // - // This is inherently racey. - if s.Status == Running || s.Status == Created { - // Send signal 0 to check if process exists. - if err := s.Signal(0); err != nil { - // Process no longer exists. - s.Status = Stopped - s.Pid = 0 - } - } - return &s, nil -} - -// List returns all sandbox ids in the given root directory. -func List(rootDir string) ([]string, error) { - log.Debugf("List sandboxes %q", rootDir) - fs, err := ioutil.ReadDir(rootDir) - if err != nil { - return nil, fmt.Errorf("ReadDir(%s) failed: %v", rootDir, err) - } - var out []string - for _, f := range fs { - out = append(out, f.Name()) - } - return out, nil -} - -// State returns the metadata of the sandbox. -func (s *Sandbox) State() specs.State { - return specs.State{ - Version: specs.Version, - ID: s.ID, - Status: s.Status.String(), - Pid: s.Pid, - Bundle: s.BundleDir, - } + return s, nil } // Start starts running the containerized process inside the sandbox. -func (s *Sandbox) Start(conf *boot.Config) error { +func (s *Sandbox) Start(cid string, spec *specs.Spec, conf *boot.Config) error { log.Debugf("Start sandbox %q, pid: %d", s.ID, s.Pid) - if s.Status != Created { - return fmt.Errorf("cannot start container in state %s", s.Status) - } - - // "If any prestart hook fails, the runtime MUST generate an error, - // stop and destroy the container". - if s.Spec.Hooks != nil { - if err := executeHooks(s.Spec.Hooks.Prestart, s.State()); err != nil { - s.Destroy() - return err - } - } - - c, err := s.connect() + conn, err := s.connect() if err != nil { - s.Destroy() return err } - defer c.Close() + defer conn.Close() // Configure the network. - if err := setupNetwork(c, s.Pid, s.Spec, conf); err != nil { - s.Destroy() + if err := setupNetwork(conn, s.Pid, spec, conf); err != nil { return fmt.Errorf("error setting up network: %v", err) } // Send a message to the sandbox control server to start the // application. - if err := c.Call(boot.ApplicationStart, nil, nil); err != nil { - s.Destroy() - return fmt.Errorf("error starting application %v: %v", s.Spec.Process.Args, err) - } - - // "If any poststart hook fails, the runtime MUST log a warning, but - // the remaining hooks and lifecycle continue as if the hook had - // succeeded". - if s.Spec.Hooks != nil { - executeHooksBestEffort(s.Spec.Hooks.Poststart, s.State()) + // + // TODO: Pass in the container id (cid) here. The sandbox + // should start only that container. + if err := conn.Call(boot.ApplicationStart, nil, nil); err != nil { + return fmt.Errorf("error starting application %v: %v", spec.Process.Args, err) } - s.Status = Running - return s.save() + return nil } -// Processes retrieves the list of processes and associated metadata inside a -// sandbox. -func (s *Sandbox) Processes() ([]*control.Process, error) { - if s.Status != Running { - return nil, fmt.Errorf("cannot get processes of container %q because it isn't running. It is in state %v", s.ID, s.Status) - } - - c, err := s.connect() +// Processes retrieves the list of processes and associated metadata for a +// given container in this sandbox. +func (s *Sandbox) Processes(cid string) ([]*control.Process, error) { + conn, err := s.connect() if err != nil { return nil, err } - defer c.Close() + defer conn.Close() var pl []*control.Process - if err := c.Call(boot.ApplicationProcesses, nil, &pl); err != nil { + // TODO: Pass in the container id (cid) here. The sandbox + // should return process info for only that container. + if err := conn.Call(boot.ApplicationProcesses, nil, &pl); err != nil { return nil, fmt.Errorf("error retrieving process data from sandbox: %v", err) } return pl, nil } -// Execute runs the specified command in the sandbox. -func (s *Sandbox) Execute(e *control.ExecArgs) (syscall.WaitStatus, error) { - log.Debugf("Execute in sandbox %q, pid: %d, args: %+v", s.ID, s.Pid, e) - if s.Status != Created && s.Status != Running { - return 0, fmt.Errorf("cannot exec in container in state %s", s.Status) - } - - log.Debugf("Connecting to sandbox...") - c, err := s.connect() +// Execute runs the specified command in the container. +func (s *Sandbox) Execute(cid string, e *control.ExecArgs) (syscall.WaitStatus, error) { + conn, err := s.connect() if err != nil { return 0, fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err) } - defer c.Close() + defer conn.Close() // Send a message to the sandbox control server to start the application. var waitStatus uint32 - if err := c.Call(boot.ApplicationExecute, e, &waitStatus); err != nil { + // TODO: Pass in the container id (cid) here. The sandbox + // should execute in the context of that container. + if err := conn.Call(boot.ApplicationExecute, e, &waitStatus); err != nil { return 0, fmt.Errorf("error executing in sandbox: %v", err) } @@ -347,60 +145,45 @@ func (s *Sandbox) Execute(e *control.ExecArgs) (syscall.WaitStatus, error) { } // Event retrieves stats about the sandbox such as memory and CPU utilization. -func (s *Sandbox) Event() (*boot.Event, error) { - if s.Status != Running && s.Status != Created { - return nil, fmt.Errorf("cannot get events for container in state: %s", s.Status) - } - - c, err := s.connect() +func (s *Sandbox) Event(cid string) (*boot.Event, error) { + conn, err := s.connect() if err != nil { return nil, err } - defer c.Close() + defer conn.Close() var e boot.Event - if err := c.Call(boot.ApplicationEvent, nil, &e); err != nil { + // TODO: Pass in the container id (cid) here. The sandbox + // should return events only for that container. + if err := conn.Call(boot.ApplicationEvent, nil, &e); err != nil { return nil, fmt.Errorf("error retrieving event data from sandbox: %v", err) } - e.ID = s.ID + e.ID = cid return &e, nil } func (s *Sandbox) connect() (*urpc.Client, error) { log.Debugf("Connecting to sandbox...") - c, err := client.ConnectTo(boot.ControlSocketAddr(s.ID)) + conn, err := client.ConnectTo(boot.ControlSocketAddr(s.ID)) if err != nil { return nil, fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err) } - return c, nil -} - -func (s *Sandbox) createProcesses(conf *boot.Config, args []string) error { - binPath, err := specutils.BinPath() - if err != nil { - return err - } - - ioFiles, err := s.createGoferProcess(conf, binPath, args) - if err != nil { - return err - } - return s.createSandboxProcess(conf, binPath, args, ioFiles) + return conn, nil } -func (s *Sandbox) createGoferProcess(conf *boot.Config, binPath string, commonArgs []string) ([]*os.File, error) { +func (s *Sandbox) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir, binPath string) ([]*os.File, error) { if conf.FileAccess != boot.FileAccessProxy { // Don't start a gofer. The sandbox will access host FS directly. return nil, nil } - var args []string - args = append(args, commonArgs...) - args = append(args, "gofer", "--bundle", s.BundleDir) + // Start with the general config flags. + args := conf.ToFlags() + args = append(args, "gofer", "--bundle", bundleDir) - // Start with root mount and then add any other additional mount. + // Add root mount and then add any other additional mounts. mountCount := 1 - for _, m := range s.Spec.Mounts { + for _, m := range spec.Mounts { if specutils.Is9PMount(m) { mountCount++ } @@ -429,8 +212,8 @@ func (s *Sandbox) createGoferProcess(conf *boot.Config, binPath string, commonAr // Setup any uid/gid mappings, and create or join the configured user // namespace so the gofer's view of the filesystem aligns with the // users in the sandbox. - setUIDGIDMappings(cmd, s.Spec) - nss := filterNS([]specs.LinuxNamespaceType{specs.UserNamespace}, s.Spec) + setUIDGIDMappings(cmd, spec) + nss := filterNS([]specs.LinuxNamespaceType{specs.UserNamespace}, spec) // Start the gofer in the given namespace. log.Debugf("Starting gofer: %s %v", binPath, args) @@ -444,7 +227,7 @@ func (s *Sandbox) createGoferProcess(conf *boot.Config, binPath string, commonAr // createSandboxProcess starts the sandbox as a subprocess by running the "boot" // command, passing in the bundle dir. -func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, commonArgs []string, ioFiles []*os.File) error { +func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, binPath string, ioFiles []*os.File) error { // nextFD is used to get unused FDs that we can pass to the sandbox. It // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 @@ -457,13 +240,13 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) } - consoleEnabled := s.ConsoleSocket != "" + consoleEnabled := consoleSocket != "" - cmd := exec.Command(binPath, commonArgs...) + cmd := exec.Command(binPath, conf.ToFlags()...) cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.Args = append(cmd.Args, "boot", - "--bundle", s.BundleDir, + "--bundle", bundleDir, "--controller-fd="+strconv.Itoa(nextFD), fmt.Sprintf("--console=%t", consoleEnabled)) nextFD++ @@ -485,9 +268,9 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common if consoleEnabled { // setupConsole will send the master on the socket, and return // the slave. - tty, err := setupConsole(s.ConsoleSocket) + tty, err := setupConsole(consoleSocket) if err != nil { - return fmt.Errorf("error setting up control socket %q: %v", s.ConsoleSocket, err) + return fmt.Errorf("error setting up control socket %q: %v", consoleSocket, err) } defer tty.Close() @@ -535,7 +318,7 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common // Joins the network namespace if network is enabled. the sandbox talks // directly to the host network, which may have been configured in the // namespace. - if ns, ok := getNS(specs.NetworkNamespace, s.Spec); ok && conf.Network != boot.NetworkNone { + if ns, ok := getNS(specs.NetworkNamespace, spec); ok && conf.Network != boot.NetworkNone { log.Infof("Sandbox will be started in the container's network namespace: %+v", ns) nss = append(nss, ns) } else { @@ -549,10 +332,10 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common // - Gofer: when using a Gofer, the sandbox process can run isolated in an // empty namespace. if conf.Network == boot.NetworkHost || conf.FileAccess == boot.FileAccessDirect { - if userns, ok := getNS(specs.UserNamespace, s.Spec); ok { + if userns, ok := getNS(specs.UserNamespace, spec); ok { log.Infof("Sandbox will be started in container's user namespace: %+v", userns) nss = append(nss, userns) - setUIDGIDMappings(cmd, s.Spec) + setUIDGIDMappings(cmd, spec) } else { log.Infof("Sandbox will be started in the current user namespace") } @@ -596,8 +379,10 @@ func (s *Sandbox) waitForCreated(timeout time.Duration) error { } // Wait waits for the containerized process to exit, and returns its WaitStatus. -func (s *Sandbox) Wait() (syscall.WaitStatus, error) { - log.Debugf("Wait on sandbox %q with pid %d", s.ID, s.Pid) +func (s *Sandbox) Wait(cid string) (syscall.WaitStatus, error) { + // TODO: This waits on the sandbox process. We need a way + // to wait on an individual container in the sandbox. + p, err := os.FindProcess(s.Pid) if err != nil { // "On Unix systems, FindProcess always succeeds and returns a @@ -611,6 +396,13 @@ func (s *Sandbox) Wait() (syscall.WaitStatus, error) { return ps.Sys().(syscall.WaitStatus), nil } +// Stop stops the container in the sandbox. +func (s *Sandbox) Stop(cid string) error { + // TODO: This should stop the container with the given ID + // in the sandbox. + return nil +} + // Destroy frees all resources associated with the sandbox. func (s *Sandbox) Destroy() error { log.Debugf("Destroy sandbox %q", s.ID) @@ -625,60 +417,26 @@ func (s *Sandbox) Destroy() error { sendSignal(s.GoferPid, unix.SIGKILL) s.GoferPid = 0 } - if err := os.RemoveAll(s.SandboxRoot); err != nil { - log.Warningf("Failed to delete sandbox root directory %q, err: %v", s.SandboxRoot, err) - } - - // "If any poststop hook fails, the runtime MUST log a warning, but the - // remaining hooks and lifecycle continue as if the hook had succeeded". - if s.Spec.Hooks != nil && (s.Status == Created || s.Status == Running) { - executeHooksBestEffort(s.Spec.Hooks.Poststop, s.State()) - } - s.Status = Stopped return nil } -// Signal sends the signal to the sandbox. -func (s *Sandbox) Signal(sig syscall.Signal) error { +// Signal sends the signal to a container in the sandbox. +func (s *Sandbox) Signal(cid string, sig syscall.Signal) error { log.Debugf("Signal sandbox %q", s.ID) - if s.Status == Stopped { - log.Warningf("sandbox %q not running, not sending signal %v to pid %d", s.ID, sig, s.Pid) - return nil - } + + // TODO: This sends a signal to the sandbox process, which + // will be forwarded to the first process in the sandbox. We need a way + // to send a signal to any container in the sandbox. + // to wait on an individual container in the sandbox. + return sendSignal(s.Pid, sig) } +// sendSignal sends a signal to the sandbox process. func sendSignal(pid int, sig syscall.Signal) error { if err := syscall.Kill(pid, sig); err != nil { return fmt.Errorf("error sending signal %d to pid %d: %v", sig, pid, err) } return nil } - -// save saves the sandbox metadata to a file. -func (s *Sandbox) save() error { - log.Debugf("Save sandbox %q", s.ID) - if err := os.MkdirAll(s.SandboxRoot, 0711); err != nil { - return fmt.Errorf("error creating sandbox root directory %q: %v", s.SandboxRoot, err) - } - meta, err := json.Marshal(s) - if err != nil { - return fmt.Errorf("error marshaling sandbox metadata: %v", err) - } - metaFile := filepath.Join(s.SandboxRoot, metadataFilename) - if err := ioutil.WriteFile(metaFile, meta, 0640); err != nil { - return fmt.Errorf("error writing sandbox metadata: %v", err) - } - return nil -} - -// exists returns true if the given file exists. -func exists(f string) bool { - if _, err := os.Stat(f); err == nil { - return true - } else if !os.IsNotExist(err) { - log.Warningf("error checking for file %q: %v", f, err) - } - return false -} diff --git a/runsc/sandbox/sandbox_test.go b/runsc/sandbox/sandbox_test.go deleted file mode 100644 index 1fac38a29..000000000 --- a/runsc/sandbox/sandbox_test.go +++ /dev/null @@ -1,665 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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 sandbox_test - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "os/signal" - "path/filepath" - "reflect" - "strings" - "syscall" - "testing" - "time" - - "context" - "flag" - "github.com/google/subcommands" - specs "github.com/opencontainers/runtime-spec/specs-go" - "golang.org/x/sys/unix" - "gvisor.googlesource.com/gvisor/pkg/abi/linux" - "gvisor.googlesource.com/gvisor/pkg/log" - "gvisor.googlesource.com/gvisor/pkg/sentry/control" - "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" - "gvisor.googlesource.com/gvisor/pkg/unet" - "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/cmd" - "gvisor.googlesource.com/gvisor/runsc/sandbox" -) - -func init() { - log.SetLevel(log.Debug) -} - -// writeSpec writes the spec to disk in the given directory. -func writeSpec(dir string, spec *specs.Spec) error { - b, err := json.Marshal(spec) - if err != nil { - return err - } - return ioutil.WriteFile(filepath.Join(dir, "config.json"), b, 0755) -} - -// newSpecWithArgs creates a simple spec with the given args suitable for use -// in tests. -func newSpecWithArgs(args ...string) *specs.Spec { - spec := &specs.Spec{ - // The host filesystem root is the sandbox root. - Root: &specs.Root{ - Path: "/", - Readonly: true, - }, - Process: &specs.Process{ - Args: args, - Env: []string{ - "PATH=" + os.Getenv("PATH"), - }, - }, - } - return spec -} - -// shutdownSignal will be sent to the sandbox in order to shut down cleanly. -const shutdownSignal = syscall.SIGUSR2 - -// setupSandbox creates a bundle and root dir for the sandbox, generates a test -// config, and writes the spec to config.json in the bundle dir. -func setupSandbox(spec *specs.Spec) (rootDir, bundleDir string, conf *boot.Config, err error) { - rootDir, err = ioutil.TempDir("", "sandboxes") - if err != nil { - return "", "", nil, fmt.Errorf("error creating root dir: %v", err) - } - - bundleDir, err = ioutil.TempDir("", "bundle") - if err != nil { - return "", "", nil, fmt.Errorf("error creating bundle dir: %v", err) - } - - if err = writeSpec(bundleDir, spec); err != nil { - return "", "", nil, fmt.Errorf("error writing spec: %v", err) - } - - conf = &boot.Config{ - RootDir: rootDir, - Network: boot.NetworkNone, - } - - return rootDir, bundleDir, conf, nil -} - -// uniqueSandboxID generates a unique sandbox id for each test. -// -// The sandbox id is used to create an abstract unix domain socket, which must -// be unique. While the sandbox forbids creating two sandboxes with the same -// name, sometimes between test runs the socket does not get cleaned up quickly -// enough, causing sandbox creation to fail. -func uniqueSandboxID() string { - return fmt.Sprintf("test-sandbox-%d", time.Now().UnixNano()) -} - -// waitForProcessList waits for the given process list to show up in the sandbox. -func waitForProcessList(s *sandbox.Sandbox, expected []*control.Process) error { - var got []*control.Process - for start := time.Now(); time.Now().Sub(start) < 10*time.Second; { - var err error - got, err := s.Processes() - if err != nil { - return fmt.Errorf("error getting process data from sandbox: %v", err) - } - if procListsEqual(got, expected) { - return nil - } - // Process might not have started, try again... - time.Sleep(10 * time.Millisecond) - } - return fmt.Errorf("sandbox got process list: %s, want: %s", procListToString(got), procListToString(expected)) -} - -// TestLifecycle tests the basic Create/Start/Signal/Destroy sandbox lifecycle. -// It verifies after each step that the sandbox can be loaded from disk, and -// has the correct status. -func TestLifecycle(t *testing.T) { - // The sandbox will just sleep for a long time. We will kill it before - // it finishes sleeping. - spec := newSpecWithArgs("sleep", "100") - - rootDir, bundleDir, conf, err := setupSandbox(spec) - if err != nil { - t.Fatalf("error setting up sandbox: %v", err) - } - defer os.RemoveAll(rootDir) - defer os.RemoveAll(bundleDir) - - // expectedPL lists the expected process state of the sandbox. - expectedPL := []*control.Process{ - { - UID: 0, - PID: 1, - PPID: 0, - C: 0, - Cmd: "sleep", - }, - } - // Create the sandbox. - id := uniqueSandboxID() - if _, err := sandbox.Create(id, spec, conf, bundleDir, "", "", nil); err != nil { - t.Fatalf("error creating sandbox: %v", err) - } - // Load the sandbox from disk and check the status. - s, err := sandbox.Load(rootDir, id) - if err != nil { - t.Fatalf("error loading sandbox: %v", err) - } - if got, want := s.Status, sandbox.Created; got != want { - t.Errorf("sandbox status got %v, want %v", got, want) - } - - // List should return the sandbox id. - ids, err := sandbox.List(rootDir) - if err != nil { - t.Fatalf("error listing sandboxes: %v", err) - } - if got, want := ids, []string{id}; !reflect.DeepEqual(got, want) { - t.Errorf("sandbox list got %v, want %v", got, want) - } - - // Start the sandbox. - if err := s.Start(conf); err != nil { - t.Fatalf("error starting sandbox: %v", err) - } - // Load the sandbox from disk and check the status. - s, err = sandbox.Load(rootDir, id) - if err != nil { - t.Fatalf("error loading sandbox: %v", err) - } - if got, want := s.Status, sandbox.Running; got != want { - t.Errorf("sandbox status got %v, want %v", got, want) - } - - // Verify that "sleep 100" is running. - if err := waitForProcessList(s, expectedPL); err != nil { - t.Error(err) - } - - // Send the sandbox a signal, which we catch and use to cleanly - // shutdown. - if err := s.Signal(shutdownSignal); err != nil { - t.Fatalf("error sending signal %v to sandbox: %v", shutdownSignal, err) - } - // Wait for it to die. - if _, err := s.Wait(); err != nil { - t.Fatalf("error waiting on sandbox: %v", err) - } - // Load the sandbox from disk and check the status. - s, err = sandbox.Load(rootDir, id) - if err != nil { - t.Fatalf("error loading sandbox: %v", err) - } - if got, want := s.Status, sandbox.Stopped; got != want { - t.Errorf("sandbox status got %v, want %v", got, want) - } - - // Destroy the sandbox. - if err := s.Destroy(); err != nil { - t.Fatalf("error destroying sandbox: %v", err) - } - - // List should not return the sandbox id. - ids, err = sandbox.List(rootDir) - if err != nil { - t.Fatalf("error listing sandboxes: %v", err) - } - if len(ids) != 0 { - t.Errorf("expected sandbox list to be empty, but got %v", ids) - } - - // Loading the sandbox by id should fail. - if _, err = sandbox.Load(rootDir, id); err == nil { - t.Errorf("expected loading destroyed sandbox to fail, but it did not") - } -} - -// Test the we can execute the application with different path formats. -func TestExePath(t *testing.T) { - for _, test := range []struct { - path string - success bool - }{ - {path: "true", success: true}, - {path: "bin/true", success: true}, - {path: "/bin/true", success: true}, - {path: "thisfiledoesntexit", success: false}, - {path: "bin/thisfiledoesntexit", success: false}, - {path: "/bin/thisfiledoesntexit", success: false}, - } { - spec := newSpecWithArgs(test.path) - rootDir, bundleDir, conf, err := setupSandbox(spec) - if err != nil { - t.Fatalf("exec: %s, error setting up sandbox: %v", test.path, err) - } - - ws, err := sandbox.Run(uniqueSandboxID(), spec, conf, bundleDir, "", "", nil) - - os.RemoveAll(rootDir) - os.RemoveAll(bundleDir) - - if test.success { - if err != nil { - t.Errorf("exec: %s, error running sandbox: %v", test.path, err) - } - if ws.ExitStatus() != 0 { - t.Errorf("exec: %s, got exit status %v want %v", test.path, ws.ExitStatus(), 0) - } - } else { - if err == nil { - t.Errorf("exec: %s, got: no error, want: error", test.path) - } - } - } -} - -// Test the we can retrieve the application exit status from the sandbox. -func TestAppExitStatus(t *testing.T) { - // First sandbox will succeed. - succSpec := newSpecWithArgs("true") - - rootDir, bundleDir, conf, err := setupSandbox(succSpec) - if err != nil { - t.Fatalf("error setting up sandbox: %v", err) - } - defer os.RemoveAll(rootDir) - defer os.RemoveAll(bundleDir) - - ws, err := sandbox.Run(uniqueSandboxID(), succSpec, conf, bundleDir, "", "", nil) - if err != nil { - t.Fatalf("error running sandbox: %v", err) - } - if ws.ExitStatus() != 0 { - t.Errorf("got exit status %v want %v", ws.ExitStatus(), 0) - } - - // Second sandbox exits with non-zero status. - wantStatus := 123 - errSpec := newSpecWithArgs("bash", "-c", fmt.Sprintf("exit %d", wantStatus)) - - rootDir2, bundleDir2, conf, err := setupSandbox(errSpec) - if err != nil { - t.Fatalf("error setting up sandbox: %v", err) - } - defer os.RemoveAll(rootDir2) - defer os.RemoveAll(bundleDir2) - - ws, err = sandbox.Run(uniqueSandboxID(), succSpec, conf, bundleDir2, "", "", nil) - if err != nil { - t.Fatalf("error running sandbox: %v", err) - } - if ws.ExitStatus() != wantStatus { - t.Errorf("got exit status %v want %v", ws.ExitStatus(), wantStatus) - } -} - -// TestExec verifies that a sandbox can exec a new program. -func TestExec(t *testing.T) { - const uid = 343 - spec := newSpecWithArgs("sleep", "100") - - rootDir, bundleDir, conf, err := setupSandbox(spec) - if err != nil { - t.Fatalf("error setting up sandbox: %v", err) - } - defer os.RemoveAll(rootDir) - defer os.RemoveAll(bundleDir) - - // Create and start the sandbox. - s, err := sandbox.Create(uniqueSandboxID(), spec, conf, bundleDir, "", "", nil) - if err != nil { - t.Fatalf("error creating sandbox: %v", err) - } - defer s.Destroy() - if err := s.Start(conf); err != nil { - t.Fatalf("error starting sandbox: %v", err) - } - - // expectedPL lists the expected process state of the sandbox. - expectedPL := []*control.Process{ - { - UID: 0, - PID: 1, - PPID: 0, - C: 0, - Cmd: "sleep", - }, - { - UID: uid, - PID: 2, - PPID: 0, - C: 0, - Cmd: "sleep", - }, - } - - // Verify that "sleep 100" is running. - if err := waitForProcessList(s, expectedPL[:1]); err != nil { - t.Error(err) - } - - execArgs := control.ExecArgs{ - Filename: "/bin/sleep", - Argv: []string{"sleep", "5"}, - Envv: []string{"PATH=" + os.Getenv("PATH")}, - WorkingDirectory: "/", - KUID: uid, - } - - // Verify that "sleep 100" and "sleep 5" are running after exec. - // First, start running exec (whick blocks). - status := make(chan error, 1) - go func() { - exitStatus, err := s.Execute(&execArgs) - if err != nil { - status <- err - } else if exitStatus != 0 { - status <- fmt.Errorf("failed with exit status: %v", exitStatus) - } else { - status <- nil - } - }() - - if err := waitForProcessList(s, expectedPL); err != nil { - t.Fatal(err) - } - - // Ensure that exec finished without error. - select { - case <-time.After(10 * time.Second): - t.Fatalf("sandbox timed out waiting for exec to finish.") - case st := <-status: - if st != nil { - t.Errorf("sandbox failed to exec %v: %v", execArgs, err) - } - } -} - -// TestCapabilities verifies that: -// - Running exec as non-root UID and GID will result in an error (because the -// executable file can't be read). -// - Running exec as non-root with CAP_DAC_OVERRIDE succeeds because it skips -// this check. -func TestCapabilities(t *testing.T) { - const uid = 343 - const gid = 2401 - spec := newSpecWithArgs("sleep", "100") - - // We generate files in the host temporary directory. - spec.Mounts = append(spec.Mounts, specs.Mount{ - Destination: os.TempDir(), - Source: os.TempDir(), - Type: "bind", - }) - - rootDir, bundleDir, conf, err := setupSandbox(spec) - if err != nil { - t.Fatalf("error setting up sandbox: %v", err) - } - defer os.RemoveAll(rootDir) - defer os.RemoveAll(bundleDir) - - // Create and start the sandbox. - s, err := sandbox.Create(uniqueSandboxID(), spec, conf, bundleDir, "", "", nil) - if err != nil { - t.Fatalf("error creating sandbox: %v", err) - } - defer s.Destroy() - if err := s.Start(conf); err != nil { - t.Fatalf("error starting sandbox: %v", err) - } - - // expectedPL lists the expected process state of the sandbox. - expectedPL := []*control.Process{ - { - UID: 0, - PID: 1, - PPID: 0, - C: 0, - Cmd: "sleep", - }, - { - UID: uid, - PID: 2, - PPID: 0, - C: 0, - Cmd: "exe", - }, - } - if err := waitForProcessList(s, expectedPL[:1]); err != nil { - t.Fatalf("Failed to wait for sleep to start, err: %v", err) - } - - // Create an executable that can't be run with the specified UID:GID. - // This shouldn't be callable within the sandbox until we add the - // CAP_DAC_OVERRIDE capability to skip the access check. - exePath := filepath.Join(rootDir, "exe") - if err := ioutil.WriteFile(exePath, []byte("#!/bin/sh\necho hello"), 0770); err != nil { - t.Fatalf("couldn't create executable: %v", err) - } - defer os.Remove(exePath) - - // Need to traverse the intermediate directory. - os.Chmod(rootDir, 0755) - - execArgs := control.ExecArgs{ - Filename: exePath, - Argv: []string{exePath}, - Envv: []string{"PATH=" + os.Getenv("PATH")}, - WorkingDirectory: "/", - KUID: uid, - KGID: gid, - Capabilities: &auth.TaskCapabilities{}, - } - - // "exe" should fail because we don't have the necessary permissions. - if _, err := s.Execute(&execArgs); err == nil { - t.Fatalf("sandbox executed without error, but an error was expected") - } - - // Now we run with the capability enabled and should succeed. - execArgs.Capabilities = &auth.TaskCapabilities{ - EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), - } - // "exe" should not fail this time. - if _, err := s.Execute(&execArgs); err != nil { - t.Fatalf("sandbox failed to exec %v: %v", execArgs, err) - } -} - -// Test that an tty FD is sent over the console socket if one is provided. -func TestConsoleSocket(t *testing.T) { - spec := newSpecWithArgs("true") - rootDir, bundleDir, conf, err := setupSandbox(spec) - if err != nil { - t.Fatalf("error setting up sandbox: %v", err) - } - defer os.RemoveAll(rootDir) - defer os.RemoveAll(bundleDir) - - // Create a named socket and start listening. We use a relative path - // to avoid overflowing the unix path length limit (108 chars). - socketPath := filepath.Join(bundleDir, "socket") - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("error getting cwd: %v", err) - } - socketRelPath, err := filepath.Rel(cwd, socketPath) - if err != nil { - t.Fatalf("error getting relative path for %q from cwd %q: %v", socketPath, cwd, err) - } - if len(socketRelPath) > len(socketPath) { - socketRelPath = socketPath - } - srv, err := unet.BindAndListen(socketRelPath, false) - if err != nil { - t.Fatalf("error binding and listening to socket %q: %v", socketPath, err) - } - defer os.Remove(socketPath) - - // Create the sandbox and pass the socket name. - id := uniqueSandboxID() - s, err := sandbox.Create(id, spec, conf, bundleDir, socketRelPath, "", nil) - if err != nil { - t.Fatalf("error creating sandbox: %v", err) - } - - // Open the othe end of the socket. - sock, err := srv.Accept() - if err != nil { - t.Fatalf("error accepting socket connection: %v", err) - } - - // Allow 3 fds to be received. We only expect 1. - r := sock.Reader(true /* blocking */) - r.EnableFDs(1) - - // The socket is closed right after sending the FD, so EOF is - // an allowed error. - b := [][]byte{{}} - if _, err := r.ReadVec(b); err != nil && err != io.EOF { - t.Fatalf("error reading from socket connection: %v", err) - } - - // We should have gotten a control message. - fds, err := r.ExtractFDs() - if err != nil { - t.Fatalf("error extracting fds from socket connection: %v", err) - } - if len(fds) != 1 { - t.Fatalf("got %d fds from socket, wanted 1", len(fds)) - } - - // Verify that the fd is a terminal. - if _, err := unix.IoctlGetTermios(fds[0], unix.TCGETS); err != nil { - t.Errorf("fd is not a terminal (ioctl TGGETS got %v)", err) - } - - // Shut it down. - if err := s.Destroy(); err != nil { - t.Fatalf("error destroying sandbox: %v", err) - } - - // Close socket. - if err := srv.Close(); err != nil { - t.Fatalf("error destroying sandbox: %v", err) - } -} - -func TestSpecUnsupported(t *testing.T) { - spec := newSpecWithArgs("/bin/true") - spec.Process.SelinuxLabel = "somelabel" - - // These are normally set by docker and will just cause warnings to be logged. - spec.Process.ApparmorProfile = "someprofile" - spec.Linux = &specs.Linux{Seccomp: &specs.LinuxSeccomp{}} - - rootDir, bundleDir, conf, err := setupSandbox(spec) - if err != nil { - t.Fatalf("error setting up sandbox: %v", err) - } - defer os.RemoveAll(rootDir) - defer os.RemoveAll(bundleDir) - - id := uniqueSandboxID() - _, err = sandbox.Create(id, spec, conf, bundleDir, "", "", nil) - if err == nil || !strings.Contains(err.Error(), "is not supported") { - t.Errorf("sandbox.Create() wrong error, got: %v, want: *is not supported, spec.Process: %+v", err, spec.Process) - } -} - -// procListsEqual is used to check whether 2 Process lists are equal for all -// implemented fields. -func procListsEqual(got, want []*control.Process) bool { - if len(got) != len(want) { - return false - } - for i := range got { - pd1 := got[i] - pd2 := want[i] - // Zero out unimplemented and timing dependant fields. - pd1.Time, pd2.Time = "", "" - pd1.STime, pd2.STime = "", "" - pd1.C, pd2.C = 0, 0 - if *pd1 != *pd2 { - return false - } - } - return true -} - -func procListToString(pl []*control.Process) string { - strs := make([]string, 0, len(pl)) - for _, p := range pl { - strs = append(strs, fmt.Sprintf("%+v", p)) - } - return fmt.Sprintf("[%s]", strings.Join(strs, ",")) -} - -// TestMain acts like runsc if it is called with the "boot" argument, otherwise -// it just runs the tests. This is required because creating a sandbox will -// call "/proc/self/exe boot". Normally /proc/self/exe is the runsc binary, -// but for tests we have to fake it. -func TestMain(m *testing.M) { - // exit writes coverage data before exiting. - exit := func(status int) { - os.Exit(status) - } - - if !flag.Parsed() { - flag.Parse() - } - - // If we are passed one of the commands then run it. - subcommands.Register(new(cmd.Boot), "boot") - subcommands.Register(new(cmd.Gofer), "gofer") - switch flag.Arg(0) { - case "boot", "gofer": - // Run the command in a goroutine so we can block the main - // thread waiting for shutdownSignal. - go func() { - conf := &boot.Config{ - RootDir: "unused-root-dir", - Network: boot.NetworkNone, - } - var ws syscall.WaitStatus - subcmdCode := subcommands.Execute(context.Background(), conf, &ws) - if subcmdCode != subcommands.ExitSuccess { - panic(fmt.Sprintf("command failed to execute, err: %v", subcmdCode)) - } - // Sandbox exited normally. Shut down this process. - os.Exit(ws.ExitStatus()) - }() - - // Shutdown cleanly when the shutdownSignal is received. This - // allows us to write coverage data before exiting. - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, shutdownSignal) - <-sigc - exit(0) - default: - // Otherwise run the tests. - exit(m.Run()) - } -} diff --git a/runsc/sandbox/status.go b/runsc/sandbox/status.go deleted file mode 100644 index 6fc936aba..000000000 --- a/runsc/sandbox/status.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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 sandbox - -// Status enumerates sandbox statuses. The statuses and their semantics are -// part of the runtime CLI spec. -// -// TODO: Get precise about the transitions between statuses. -type Status int - -const ( - // Creating indicates "the container is being created". - Creating Status = iota - - // Created indicates "the runtime has finished the create operation and - // the container process has neither exited nor executed the - // user-specified program". - Created - - // Running indicates "the container process has executed the - // user-specified program but has not exited". - Running - - // Stopped indicates "the container process has exited". - Stopped -) - -// String converts a Status to a string. These strings are part of the runtime -// CLI spec and should not be changed. -func (s Status) String() string { - switch s { - case Creating: - return "creating" - case Created: - return "created" - case Running: - return "running" - case Stopped: - return "stopped" - default: - return "unknown" - } - -} diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index dcb4b20db..5f455dec4 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -41,9 +41,28 @@ func LogSpec(spec *specs.Spec) { log.Debugf("Spec.Root: %+v", spec.Root) } +// ValidateSpec validates that the spec is compatible with runsc. +func ValidateSpec(spec *specs.Spec) error { + if spec.Process == nil { + return fmt.Errorf("Process must be defined") + } + if spec.Process.SelinuxLabel != "" { + return fmt.Errorf("SELinux is not supported: %s", spec.Process.SelinuxLabel) + } + + // Docker uses AppArmor by default, so just log that it's being ignored. + if spec.Process.ApparmorProfile != "" { + log.Warningf("AppArmor profile %q is being ignored", spec.Process.ApparmorProfile) + } + + // TODO: Apply seccomp to application inside sandbox. + if spec.Linux != nil && spec.Linux.Seccomp != nil { + log.Warningf("Seccomp spec is being ignored") + } + return nil +} + // ReadSpec reads an OCI runtime spec from the given bundle directory. -// -// TODO: This should validate the spec. func ReadSpec(bundleDir string) (*specs.Spec, error) { // The spec file must be in "config.json" inside the bundle directory. specFile := filepath.Join(bundleDir, "config.json") -- cgit v1.2.3 From a0e2126be49e5eda45dcaead497129c08e08a1e5 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Mon, 4 Jun 2018 11:25:40 -0700 Subject: Refactor container_test in preparation for sandbox_test Common code to setup and run sandbox is moved to testutil. Also, don't link "boot" and "gofer" commands with test binary. Instead, use runsc binary from the build. This not only make the test setup simpler, but also resolves a dependency issue with sandbox_tests not depending on container package. PiperOrigin-RevId: 199164478 Change-Id: I27226286ca3f914d4d381358270dd7d70ee8372f --- runsc/BUILD | 3 + runsc/container/BUILD | 9 +- runsc/container/container_test.go | 172 ++++++++------------------------------ runsc/specutils/specutils.go | 8 +- runsc/test/testutil/BUILD | 17 ++++ runsc/test/testutil/testutil.go | 133 +++++++++++++++++++++++++++++ 6 files changed, 198 insertions(+), 144 deletions(-) create mode 100644 runsc/test/testutil/BUILD create mode 100644 runsc/test/testutil/testutil.go (limited to 'runsc/specutils') diff --git a/runsc/BUILD b/runsc/BUILD index 8f8e2ee35..2f0bbaf2b 100644 --- a/runsc/BUILD +++ b/runsc/BUILD @@ -8,6 +8,9 @@ go_binary( "main.go", ], pure = "on", + visibility = [ + "//runsc:__subpackages__", + ], x_defs = {"main.gitRevision": "{GIT_REVISION}"}, deps = [ "//pkg/log", diff --git a/runsc/container/BUILD b/runsc/container/BUILD index c558b4b0a..fe477abf2 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -27,18 +27,17 @@ go_test( name = "container_test", size = "small", srcs = ["container_test.go"], - pure = "on", - rundir = ".", + data = [ + "//runsc", + ], deps = [ "//pkg/abi/linux", "//pkg/log", "//pkg/sentry/control", "//pkg/sentry/kernel/auth", "//pkg/unet", - "//runsc/boot", - "//runsc/cmd", "//runsc/container", - "@com_github_google_subcommands//:go_default_library", + "//runsc/test/testutil", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", "@org_golang_x_sys//unix:go_default_library", ], diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 24e9de3ce..0844cb9df 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -15,7 +15,6 @@ package container_test import ( - "encoding/json" "fmt" "io" "io/ioutil" @@ -29,9 +28,6 @@ import ( "testing" "time" - "context" - "flag" - "github.com/google/subcommands" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" "gvisor.googlesource.com/gvisor/pkg/abi/linux" @@ -39,80 +35,15 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/control" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" "gvisor.googlesource.com/gvisor/pkg/unet" - "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/cmd" "gvisor.googlesource.com/gvisor/runsc/container" + "gvisor.googlesource.com/gvisor/runsc/test/testutil" ) func init() { log.SetLevel(log.Debug) -} - -// writeSpec writes the spec to disk in the given directory. -func writeSpec(dir string, spec *specs.Spec) error { - b, err := json.Marshal(spec) - if err != nil { - return err - } - return ioutil.WriteFile(filepath.Join(dir, "config.json"), b, 0755) -} - -// newSpecWithArgs creates a simple spec with the given args suitable for use -// in tests. -func newSpecWithArgs(args ...string) *specs.Spec { - spec := &specs.Spec{ - // The host filesystem root is the container root. - Root: &specs.Root{ - Path: "/", - Readonly: true, - }, - Process: &specs.Process{ - Args: args, - Env: []string{ - "PATH=" + os.Getenv("PATH"), - }, - }, - } - return spec -} - -// setupContainer creates a bundle and root dir for the container, generates a -// test config, and writes the spec to config.json in the bundle dir. -func setupContainer(spec *specs.Spec) (rootDir, bundleDir string, conf *boot.Config, err error) { - rootDir, err = ioutil.TempDir("", "containers") - if err != nil { - return "", "", nil, fmt.Errorf("error creating root dir: %v", err) - } - - bundleDir, err = ioutil.TempDir("", "bundle") - if err != nil { - return "", "", nil, fmt.Errorf("error creating bundle dir: %v", err) - } - - if err = writeSpec(bundleDir, spec); err != nil { - return "", "", nil, fmt.Errorf("error writing spec: %v", err) + if err := testutil.ConfigureExePath(); err != nil { + panic(err.Error()) } - - conf = &boot.Config{ - RootDir: rootDir, - Network: boot.NetworkNone, - // Don't add flags when calling subprocesses, since the test - // runner does not know about all the flags. We control the - // Config in the subprocess anyways, so it does not matter. - TestModeNoFlags: true, - } - - return rootDir, bundleDir, conf, nil -} - -// uniqueContainerID generates a unique container id for each test. -// -// The container id is used to create an abstract unix domain socket, which must -// be unique. While the container forbids creating two containers with the same -// name, sometimes between test runs the socket does not get cleaned up quickly -// enough, causing container creation to fail. -func uniqueContainerID() string { - return fmt.Sprintf("test-container-%d", time.Now().UnixNano()) } // waitForProcessList waits for the given process list to show up in the container. @@ -167,9 +98,9 @@ func procListToString(pl []*control.Process) string { func TestLifecycle(t *testing.T) { // The container will just sleep for a long time. We will kill it before // it finishes sleeping. - spec := newSpecWithArgs("sleep", "100") + spec := testutil.NewSpecWithArgs("sleep", "100") - rootDir, bundleDir, conf, err := setupContainer(spec) + rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { t.Fatalf("error setting up container: %v", err) } @@ -187,7 +118,7 @@ func TestLifecycle(t *testing.T) { }, } // Create the container. - id := uniqueContainerID() + id := testutil.UniqueContainerID() if _, err := container.Create(id, spec, conf, bundleDir, "", ""); err != nil { t.Fatalf("error creating container: %v", err) } @@ -298,13 +229,13 @@ func TestExePath(t *testing.T) { {path: "bin/thisfiledoesntexit", success: false}, {path: "/bin/thisfiledoesntexit", success: false}, } { - spec := newSpecWithArgs(test.path) - rootDir, bundleDir, conf, err := setupContainer(spec) + spec := testutil.NewSpecWithArgs(test.path) + rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { t.Fatalf("exec: %s, error setting up container: %v", test.path, err) } - ws, err := container.Run(uniqueContainerID(), spec, conf, bundleDir, "", "") + ws, err := container.Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") os.RemoveAll(rootDir) os.RemoveAll(bundleDir) @@ -327,16 +258,16 @@ func TestExePath(t *testing.T) { // Test the we can retrieve the application exit status from the container. func TestAppExitStatus(t *testing.T) { // First container will succeed. - succSpec := newSpecWithArgs("true") + succSpec := testutil.NewSpecWithArgs("true") - rootDir, bundleDir, conf, err := setupContainer(succSpec) + rootDir, bundleDir, conf, err := testutil.SetupContainer(succSpec) if err != nil { t.Fatalf("error setting up container: %v", err) } defer os.RemoveAll(rootDir) defer os.RemoveAll(bundleDir) - ws, err := container.Run(uniqueContainerID(), succSpec, conf, bundleDir, "", "") + ws, err := container.Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir, "", "") if err != nil { t.Fatalf("error running container: %v", err) } @@ -346,16 +277,16 @@ func TestAppExitStatus(t *testing.T) { // Second container exits with non-zero status. wantStatus := 123 - errSpec := newSpecWithArgs("bash", "-c", fmt.Sprintf("exit %d", wantStatus)) + errSpec := testutil.NewSpecWithArgs("bash", "-c", fmt.Sprintf("exit %d", wantStatus)) - rootDir2, bundleDir2, conf, err := setupContainer(errSpec) + rootDir2, bundleDir2, conf, err := testutil.SetupContainer(errSpec) if err != nil { t.Fatalf("error setting up container: %v", err) } defer os.RemoveAll(rootDir2) defer os.RemoveAll(bundleDir2) - ws, err = container.Run(uniqueContainerID(), succSpec, conf, bundleDir2, "", "") + ws, err = container.Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir2, "", "") if err != nil { t.Fatalf("error running container: %v", err) } @@ -367,9 +298,9 @@ func TestAppExitStatus(t *testing.T) { // TestExec verifies that a container can exec a new program. func TestExec(t *testing.T) { const uid = 343 - spec := newSpecWithArgs("sleep", "100") + spec := testutil.NewSpecWithArgs("sleep", "100") - rootDir, bundleDir, conf, err := setupContainer(spec) + rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { t.Fatalf("error setting up container: %v", err) } @@ -377,7 +308,7 @@ func TestExec(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - s, err := container.Create(uniqueContainerID(), spec, conf, bundleDir, "", "") + s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -454,7 +385,7 @@ func TestExec(t *testing.T) { func TestCapabilities(t *testing.T) { const uid = 343 const gid = 2401 - spec := newSpecWithArgs("sleep", "100") + spec := testutil.NewSpecWithArgs("sleep", "100") // We generate files in the host temporary directory. spec.Mounts = append(spec.Mounts, specs.Mount{ @@ -463,7 +394,7 @@ func TestCapabilities(t *testing.T) { Type: "bind", }) - rootDir, bundleDir, conf, err := setupContainer(spec) + rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { t.Fatalf("error setting up container: %v", err) } @@ -471,7 +402,7 @@ func TestCapabilities(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - s, err := container.Create(uniqueContainerID(), spec, conf, bundleDir, "", "") + s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -540,8 +471,8 @@ func TestCapabilities(t *testing.T) { // Test that an tty FD is sent over the console socket if one is provided. func TestConsoleSocket(t *testing.T) { - spec := newSpecWithArgs("true") - rootDir, bundleDir, conf, err := setupContainer(spec) + spec := testutil.NewSpecWithArgs("true") + rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { t.Fatalf("error setting up container: %v", err) } @@ -569,7 +500,7 @@ func TestConsoleSocket(t *testing.T) { defer os.Remove(socketPath) // Create the container and pass the socket name. - id := uniqueContainerID() + id := testutil.UniqueContainerID() s, err := container.Create(id, spec, conf, bundleDir, socketRelPath, "") if err != nil { t.Fatalf("error creating container: %v", err) @@ -618,21 +549,21 @@ func TestConsoleSocket(t *testing.T) { } func TestSpecUnsupported(t *testing.T) { - spec := newSpecWithArgs("/bin/true") + spec := testutil.NewSpecWithArgs("/bin/true") spec.Process.SelinuxLabel = "somelabel" // These are normally set by docker and will just cause warnings to be logged. spec.Process.ApparmorProfile = "someprofile" spec.Linux = &specs.Linux{Seccomp: &specs.LinuxSeccomp{}} - rootDir, bundleDir, conf, err := setupContainer(spec) + rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { t.Fatalf("error setting up container: %v", err) } defer os.RemoveAll(rootDir) defer os.RemoveAll(bundleDir) - id := uniqueContainerID() + id := testutil.UniqueContainerID() _, err = container.Create(id, spec, conf, bundleDir, "", "") if err == nil || !strings.Contains(err.Error(), "is not supported") { t.Errorf("container.Create() wrong error, got: %v, want: *is not supported, spec.Process: %+v", err, spec.Process) @@ -642,14 +573,17 @@ func TestSpecUnsupported(t *testing.T) { // TestRunNonRoot checks that sandbox can be configured when running as // non-priviledged user. func TestRunNonRoot(t *testing.T) { - spec := newSpecWithArgs("/bin/true") + spec := testutil.NewSpecWithArgs("/bin/true") spec.Process.User.UID = 343 spec.Process.User.GID = 2401 // User that container runs as can't list '$TMP/blocked' and would fail to // mount it. - dir := path.Join(os.TempDir(), "blocked") - if err := os.Mkdir(dir, 0700); err != nil { + dir, err := ioutil.TempDir("", "blocked") + if err != nil { + t.Fatalf("ioutil.TempDir() failed: %v", err) + } + if err := os.Chmod(dir, 0700); err != nil { t.Fatalf("os.MkDir(%q) failed: %v", dir, err) } dir = path.Join(dir, "test") @@ -664,7 +598,7 @@ func TestRunNonRoot(t *testing.T) { Type: "bind", }) - rootDir, bundleDir, conf, err := setupContainer(spec) + rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { t.Fatalf("error setting up container: %v", err) } @@ -672,7 +606,7 @@ func TestRunNonRoot(t *testing.T) { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - s, err := container.Create(uniqueContainerID(), spec, conf, bundleDir, "", "") + s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -688,39 +622,3 @@ func TestRunNonRoot(t *testing.T) { t.Errorf("container failed, waitStatus: %v", ws) } } - -// TestMain acts like runsc if it is called with the "boot" argument, otherwise -// it just runs the tests. This is required because creating a container will -// call "/proc/self/exe boot". Normally /proc/self/exe is the runsc binary, -// but for tests we have to fake it. -func TestMain(m *testing.M) { - // exit writes coverage data before exiting. - exit := func(status int) { - os.Exit(status) - } - - if !flag.Parsed() { - flag.Parse() - } - - // If we are passed one of the commands then run it. - subcommands.Register(new(cmd.Boot), "boot") - subcommands.Register(new(cmd.Gofer), "gofer") - switch flag.Arg(0) { - case "boot", "gofer": - conf := &boot.Config{ - RootDir: "unused-root-dir", - Network: boot.NetworkNone, - } - var ws syscall.WaitStatus - subcmdCode := subcommands.Execute(context.Background(), conf, &ws) - if subcmdCode != subcommands.ExitSuccess { - panic(fmt.Sprintf("command failed to execute, err: %v", subcmdCode)) - } - // Container exited. Shut down this process. - exit(ws.ExitStatus()) - default: - // Otherwise run the tests. - exit(m.Run()) - } -} diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 5f455dec4..3161360b4 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -32,6 +32,10 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" ) +// ExePath must point to runsc binary, which is normally the same binary. It's +// changed in tests that aren't linked in the same binary. +var ExePath = "/proc/self/exe" + // LogSpec logs the spec in a human-friendly way. func LogSpec(spec *specs.Spec) { log.Debugf("Spec: %+v", spec) @@ -197,9 +201,9 @@ func Is9PMount(m specs.Mount) bool { // BinPath returns the real path to self, resolving symbolink links. This is done // to make the process name appears as 'runsc', instead of 'exe'. func BinPath() (string, error) { - binPath, err := filepath.EvalSymlinks("/proc/self/exe") + binPath, err := filepath.EvalSymlinks(ExePath) if err != nil { - return "", fmt.Errorf(`error resolving "/proc/self/exe" symlink: %v`, err) + return "", fmt.Errorf(`error resolving %q symlink: %v`, ExePath, err) } return binPath, nil } diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD new file mode 100644 index 000000000..2c2555d98 --- /dev/null +++ b/runsc/test/testutil/BUILD @@ -0,0 +1,17 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testutil", + srcs = ["testutil.go"], + importpath = "gvisor.googlesource.com/gvisor/runsc/test/testutil", + visibility = [ + "//runsc:__subpackages__", + ], + deps = [ + "//runsc/boot", + "//runsc/specutils", + "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + ], +) diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go new file mode 100644 index 000000000..87db0a170 --- /dev/null +++ b/runsc/test/testutil/testutil.go @@ -0,0 +1,133 @@ +// Copyright 2018 Google Inc. +// +// 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 testutil contains utility functions for runsc tests. +package testutil + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/specutils" +) + +// ConfigureExePath configures the executable for runsc in the test environment. +func ConfigureExePath() error { + + // runsc is in a directory like: 'runsc/linux_amd64_pure_stripped/runsc'. + // Since I don't want to construct 'linux_amd64_pure_stripped' based on the + // build type, do a quick search for: 'runsc/*/runsc' + exePath := "" + lv1 := "./runsc" + lv1fis, err := ioutil.ReadDir(lv1) + if err != nil { + return err + } + for _, fi := range lv1fis { + if !fi.IsDir() { + continue + } + lv2fis, err := ioutil.ReadDir(filepath.Join(lv1, fi.Name())) + if err != nil { + return err + } + for _, candidate := range lv2fis { + if !candidate.IsDir() && candidate.Name() == "runsc" { + exePath, err = filepath.Abs(filepath.Join(lv1, fi.Name(), candidate.Name())) + if err != nil { + return err + } + break + } + } + } + if exePath == "" { + return fmt.Errorf("path to runsc not found") + } + specutils.ExePath = exePath + return nil +} + +// NewSpecWithArgs creates a simple spec with the given args suitable for use +// in tests. +func NewSpecWithArgs(args ...string) *specs.Spec { + spec := &specs.Spec{ + // The host filesystem root is the container root. + Root: &specs.Root{ + Path: "/", + Readonly: true, + }, + Process: &specs.Process{ + Args: args, + Env: []string{ + "PATH=" + os.Getenv("PATH"), + }, + }, + } + return spec +} + +// SetupContainer creates a bundle and root dir for the container, generates a +// test config, and writes the spec to config.json in the bundle dir. +func SetupContainer(spec *specs.Spec) (rootDir, bundleDir string, conf *boot.Config, err error) { + rootDir, err = ioutil.TempDir("", "containers") + if err != nil { + return "", "", nil, fmt.Errorf("error creating root dir: %v", err) + } + + bundleDir, err = ioutil.TempDir("", "bundle") + if err != nil { + return "", "", nil, fmt.Errorf("error creating bundle dir: %v", err) + } + + if err = writeSpec(bundleDir, spec); err != nil { + return "", "", nil, fmt.Errorf("error writing spec: %v", err) + } + + conf = &boot.Config{ + RootDir: rootDir, + Network: boot.NetworkNone, + // Don't add flags when calling subprocesses, since the test + // runner does not know about all the flags. We control the + // Config in the subprocess anyways, so it does not matter. + TestModeNoFlags: true, + } + + return rootDir, bundleDir, conf, nil +} + +// writeSpec writes the spec to disk in the given directory. +func writeSpec(dir string, spec *specs.Spec) error { + b, err := json.Marshal(spec) + if err != nil { + return err + } + return ioutil.WriteFile(filepath.Join(dir, "config.json"), b, 0755) +} + +// UniqueContainerID generates a unique container id for each test. +// +// The container id is used to create an abstract unix domain socket, which must +// be unique. While the container forbids creating two containers with the same +// name, sometimes between test runs the socket does not get cleaned up quickly +// enough, causing container creation to fail. +func UniqueContainerID() string { + return fmt.Sprintf("test-container-%d", time.Now().UnixNano()) +} -- cgit v1.2.3 From 2081c5e7f73eadb2ec84640d4b03f4eb1881950e Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Fri, 15 Jun 2018 13:57:29 -0700 Subject: runsc: support /dev bind mount which does not conflict with default /dev mount. PiperOrigin-RevId: 200768923 Change-Id: I4b8da10bcac296e8171fe6754abec5aabfec5e65 --- runsc/boot/fs.go | 59 +++++++++++++++++++------------------------- runsc/boot/loader_test.go | 45 +++++++++++++++++++++++++++++++++ runsc/specutils/specutils.go | 38 +++++++++++++++++++++++++++- 3 files changed, 108 insertions(+), 34 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 3113f1857..7786e4d4a 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -35,6 +35,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs" "gvisor.googlesource.com/gvisor/pkg/syserror" + "gvisor.googlesource.com/gvisor/runsc/specutils" ) type fdDispenser struct { @@ -78,16 +79,29 @@ func configureMounts(ctx context.Context, spec *specs.Spec, conf *Config, mns *f // Keep track of whether proc, sys, and tmp were mounted. var procMounted, sysMounted, tmpMounted bool + // Always mount /dev. + if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ + Type: "devtmpfs", + Destination: "/dev", + }); err != nil { + return err + } + + // Always mount /dev/pts. + if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ + Type: "devpts", + Destination: "/dev/pts", + }); err != nil { + return err + } + // Mount all submounts from the spec. for _, m := range spec.Mounts { - // OCI spec uses many different mounts for the things inside of '/dev'. We - // have a single mount at '/dev' that is always mounted, regardless of - // whether it was asked for, as the spec says we SHOULD. - if strings.HasPrefix(m.Destination, "/dev") { + if !specutils.IsSupportedDevMount(m) { log.Warningf("ignoring dev mount at %q", m.Destination) continue } - switch m.Destination { + switch filepath.Clean(m.Destination) { case "/proc": procMounted = true case "/sys": @@ -101,22 +115,6 @@ func configureMounts(ctx context.Context, spec *specs.Spec, conf *Config, mns *f } } - // Always mount /dev. - if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ - Type: "devtmpfs", - Destination: "/dev", - }); err != nil { - return err - } - - // Always mount /dev/pts. - if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ - Type: "devpts", - Destination: "/dev/pts", - }); err != nil { - return err - } - // Mount proc and sys even if the user did not ask for it, as the spec // says we SHOULD. if !procMounted { @@ -282,18 +280,13 @@ func mountSubmount(ctx context.Context, spec *specs.Spec, conf *Config, mns *fs. // If there are submounts, we need to overlay the mount on top of a // ramfs with stub directories for submount paths. - // - // We do not do this for /dev, since there will usually be submounts in - // the spec, but our devfs implementation contains all the necessary - // directories and files (well, most of them anyways). - if m.Destination != "/dev" { - submounts := subtargets(m.Destination, spec.Mounts) - if len(submounts) > 0 { - log.Infof("Adding submount overlay over %q", m.Destination) - inode, err = addSubmountOverlay(ctx, inode, submounts) - if err != nil { - return fmt.Errorf("error adding submount overlay: %v", err) - } + mounts := specutils.SupportedMounts(spec.Mounts) + submounts := subtargets(m.Destination, mounts) + if len(submounts) > 0 { + log.Infof("Adding submount overlay over %q", m.Destination) + inode, err = addSubmountOverlay(ctx, inode, submounts) + if err != nil { + return fmt.Errorf("error adding submount overlay: %v", err) } } diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index ca78c2cd6..a7f59f775 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -256,6 +256,51 @@ func TestCreateMountNamespace(t *testing.T) { expectedPaths: []string{"/foo", "/foo/bar", "/foo/bar/baz", "/foo/qux", "/foo/qux-quz", "/foo/some/very/very/deep/path", "/proc", "/dev", "/sys"}, }, + { + name: "mount inside /dev", + spec: specs.Spec{ + Root: &specs.Root{ + Path: os.TempDir(), + Readonly: true, + }, + Mounts: []specs.Mount{ + { + Destination: "/proc", + Type: "tmpfs", + }, + { + Destination: "/dev", + Type: "tmpfs", + }, + { + // Mounted by runsc by default. + Destination: "/dev/fd", + Type: "tmpfs", + }, + { + // Mount with the same prefix. + Destination: "/dev/fd-foo", + Source: testFile.Name(), + Type: "bind", + }, + { + // Unsupported fs type. + Destination: "/dev/mqueue", + Type: "mqueue", + }, + { + Destination: "/dev/foo", + Type: "tmpfs", + }, + { + Destination: "/dev/bar", + Source: testFile.Name(), + Type: "bind", + }, + }, + }, + expectedPaths: []string{"/proc", "/dev", "/dev/fd-foo", "/dev/foo", "/dev/bar", "/sys"}, + }, } for _, tc := range testCases { diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 3161360b4..0bb462eb5 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -195,7 +195,43 @@ func capsFromNames(names []string) (auth.CapabilitySet, error) { // Is9PMount returns true if the given mount can be mounted as an external gofer. func Is9PMount(m specs.Mount) bool { - return m.Type == "bind" && m.Source != "" && !strings.HasPrefix(m.Destination, "/dev") + return m.Type == "bind" && m.Source != "" && IsSupportedDevMount(m) +} + +// IsSupportedDevMount returns true if the mount is a supported /dev mount. +// Only mount that does not conflict with runsc default /dev mount is +// supported. +func IsSupportedDevMount(m specs.Mount) bool { + // These are devices exist inside sentry. See pkg/sentry/fs/dev/dev.go + var existingDevices = []string{ + "/dev/fd", "/dev/stdin", "/dev/stdout", "/dev/stderr", + "/dev/null", "/dev/zero", "/dev/full", "/dev/random", + "/dev/urandom", "/dev/shm", "/dev/pts", "/dev/ptmx", + } + dst := filepath.Clean(m.Destination) + if dst == "/dev" { + // OCI spec uses many different mounts for the things inside of '/dev'. We + // have a single mount at '/dev' that is always mounted, regardless of + // whether it was asked for, as the spec says we SHOULD. + return false + } + for _, dev := range existingDevices { + if dst == dev || strings.HasPrefix(dst, dev+"/") { + return false + } + } + return true +} + +// SupportedMounts filters out unsupported mounts. +func SupportedMounts(mounts []specs.Mount) []specs.Mount { + var newMounts []specs.Mount + for _, m := range mounts { + if IsSupportedDevMount(m) { + newMounts = append(newMounts, m) + } + } + return newMounts } // BinPath returns the real path to self, resolving symbolink links. This is done -- cgit v1.2.3 From f3727528e57ab720fac55553471b31877163cc12 Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Mon, 18 Jun 2018 13:36:55 -0700 Subject: runsc: support symlink to the exec path. PiperOrigin-RevId: 201049912 Change-Id: Idd937492217a4c2ca3d59c602e41576a3b203dd9 --- runsc/specutils/specutils.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runsc/specutils') diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 0bb462eb5..8dae3efb1 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -106,7 +106,9 @@ func GetExecutablePath(exec, root string, env []string) (string, error) { // for. for _, p := range path { abs := filepath.Join(root, p, exec) - if _, err := os.Stat(abs); err == nil { + // Do not follow symlink link because the target is in the container + // root filesystem. + if _, err := os.Lstat(abs); err == nil { // We found it! Return the path relative to the root. return filepath.Join("/", p, exec), nil } -- cgit v1.2.3 From 5397963b5d4d57bd3d3668df880b5314ca2fc3d8 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 19 Jun 2018 21:42:21 -0700 Subject: runsc: Enable container creation within existing sandboxes. Containers are created as processes in the sandbox. Of the many things that don't work yet, the biggest issue is that the fsgofer is launched with its root as the sandbox's root directory. Thus, when a container is started and wants to read anything (including the init binary of the container), the gofer tries to serve from sandbox's root (which basically just has pause), not the container's. PiperOrigin-RevId: 201294560 Change-Id: I6423aa8830538959c56ae908ce067e4199d627b1 --- runsc/boot/controller.go | 45 +++++++++ runsc/boot/loader.go | 193 ++++++++++++++++++++++++++------------ runsc/cmd/events.go | 2 +- runsc/cmd/exec.go | 2 +- runsc/cmd/ps.go | 2 +- runsc/container/BUILD | 1 + runsc/container/container.go | 66 +++++++++---- runsc/container/container_test.go | 73 +++++++++++++- runsc/sandbox/sandbox.go | 42 +++++++-- runsc/sandbox/sandbox_test.go | 2 +- runsc/specutils/specutils.go | 55 ++++++++++- 11 files changed, 386 insertions(+), 97 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index ae727f144..1a598199d 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -15,9 +15,12 @@ package boot import ( + "errors" "fmt" + specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.googlesource.com/gvisor/pkg/control/server" + "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/control" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" @@ -50,6 +53,10 @@ const ( // ContainerSignal is used to send a signal to a container. ContainerSignal = "containerManager.Signal" + // ContainerStart is the URPC endpoint for running a non-root container + // within a sandbox. + ContainerStart = "containerManager.Start" + // ContainerWait is used to wait on the init process of the container // and return its ExitStatus. ContainerWait = "containerManager.Wait" @@ -127,10 +134,14 @@ type containerManager struct { // watchdog is the kernel watchdog. watchdog *watchdog.Watchdog + + // l is the loader that creates containers and sandboxes. + l *Loader } // StartRoot will start the root container process. func (cm *containerManager) StartRoot(_, _ *struct{}) error { + log.Debugf("containerManager.StartRoot") // Tell the root container to start and wait for the result. cm.startChan <- struct{}{} return <-cm.startResultChan @@ -138,11 +149,42 @@ func (cm *containerManager) StartRoot(_, _ *struct{}) error { // Processes retrieves information about processes running in the sandbox. func (cm *containerManager) Processes(_, out *[]*control.Process) error { + log.Debugf("containerManager.Processes") return control.Processes(cm.k, out) } +// StartArgs contains arguments to the Start method. +type StartArgs struct { + // Spec is the spec of the container to start. + Spec *specs.Spec + + // TODO: Separate sandbox and container configs. + // Config is the runsc-specific configuration for the sandbox. + Conf *Config +} + +// Start runs a created container within a sandbox. +func (cm *containerManager) Start(args *StartArgs, _ *struct{}) error { + log.Debugf("containerManager.Start") + + // Validate arguments. + if args == nil { + return errors.New("start missing arguments") + } + if args.Spec == nil { + return errors.New("start arguments missing spec") + } + if args.Conf == nil { + return errors.New("start arguments missing config") + } + + cm.l.startContainer(args, cm.k) + return nil +} + // Execute runs a command on a created or running sandbox. func (cm *containerManager) Execute(e *control.ExecArgs, waitStatus *uint32) error { + log.Debugf("containerManager.Execute") proc := control.Proc{Kernel: cm.k} if err := proc.Exec(e, waitStatus); err != nil { return fmt.Errorf("error executing: %+v: %v", e, err) @@ -152,6 +194,7 @@ func (cm *containerManager) Execute(e *control.ExecArgs, waitStatus *uint32) err // Checkpoint pauses a sandbox and saves its state. func (cm *containerManager) Checkpoint(o *control.SaveOpts, _ *struct{}) error { + log.Debugf("containerManager.Checkpoint") state := control.State{ Kernel: cm.k, Watchdog: cm.watchdog, @@ -173,6 +216,7 @@ func (cm *containerManager) Resume(_, _ *struct{}) error { // Wait waits for the init process in the given container. func (cm *containerManager) Wait(cid *string, waitStatus *uint32) error { + log.Debugf("containerManager.Wait") // TODO: Use the cid and wait on the init process in that // container. Currently we just wait on PID 1 in the sandbox. tg := cm.k.TaskSet().Root.ThreadGroupWithID(1) @@ -195,6 +239,7 @@ type SignalArgs struct { // Signal sends a signal to the init process of the container. func (cm *containerManager) Signal(args *SignalArgs, _ *struct{}) error { + log.Debugf("containerManager.Signal") // TODO: Use the cid and send the signal to the init // process in theat container. Currently we just signal PID 1 in the // sandbox. diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 526e8f8bb..d1a413cc7 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package boot loads the kernel and runs a container.. +// Package boot loads the kernel and runs a container. package boot import ( @@ -79,8 +79,8 @@ type Loader struct { // container. It should be called when a sandbox is destroyed. stopSignalForwarding func() - // procArgs refers to the root container task. - procArgs kernel.CreateProcessArgs + // rootProcArgs refers to the root sandbox init task. + rootProcArgs kernel.CreateProcessArgs } func init() { @@ -117,12 +117,6 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in } tk.SetClocks(time.NewCalibratedClocks()) - // Create initial limits. - ls, err := createLimitSet(spec) - if err != nil { - return nil, fmt.Errorf("error creating limits: %v", err) - } - // Create capabilities. caps, err := specutils.Capabilities(spec.Process.Capabilities) if err != nil { @@ -154,13 +148,6 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in return nil, fmt.Errorf("failed to enable strace: %v", err) } - // Get the executable path, which is a bit tricky because we have to - // inspect the environment PATH which is relative to the root path. - exec, err := specutils.GetExecutablePath(spec.Process.Args[0], spec.Root.Path, spec.Process.Env) - if err != nil { - return nil, fmt.Errorf("error getting executable path: %v", err) - } - // Create an empty network stack because the network namespace may be empty at // this point. Netns is configured before Run() is called. Netstack is // configured using a control uRPC message. Host network is configured inside @@ -223,16 +210,56 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in return nil, fmt.Errorf("error creating control server: %v", err) } + // We don't care about child signals; some platforms can generate a + // tremendous number of useless ones (I'm looking at you, ptrace). + if err := sighandling.IgnoreChildStop(); err != nil { + return nil, fmt.Errorf("failed to ignore child stop signals: %v", err) + } + // Ensure that most signals received in sentry context are forwarded to + // the emulated kernel. + stopSignalForwarding := sighandling.StartForwarding(k) + + procArgs, err := newProcess(spec, conf, ioFDs, console, creds, utsns, ipcns, k) + if err != nil { + return nil, fmt.Errorf("failed to create root process: %v", err) + } + + l := &Loader{ + k: k, + ctrl: ctrl, + conf: conf, + console: console, + watchdog: watchdog, + stopSignalForwarding: stopSignalForwarding, + rootProcArgs: procArgs, + } + ctrl.manager.l = l + return l, nil +} + +// newProcess creates a process that can be run with kernel.CreateProcess. +func newProcess(spec *specs.Spec, conf *Config, ioFDs []int, console bool, creds *auth.Credentials, utsns *kernel.UTSNamespace, ipcns *kernel.IPCNamespace, k *kernel.Kernel) (kernel.CreateProcessArgs, error) { + // Create initial limits. + ls, err := createLimitSet(spec) + if err != nil { + return kernel.CreateProcessArgs{}, fmt.Errorf("error creating limits: %v", err) + } + + // Get the executable path, which is a bit tricky because we have to + // inspect the environment PATH which is relative to the root path. + exec, err := specutils.GetExecutablePath(spec.Process.Args[0], spec.Root.Path, spec.Process.Env) + if err != nil { + return kernel.CreateProcessArgs{}, fmt.Errorf("error getting executable path: %v", err) + } + // Create the process arguments. procArgs := kernel.CreateProcessArgs{ - Filename: exec, - Argv: spec.Process.Args, - Envv: spec.Process.Env, - WorkingDirectory: spec.Process.Cwd, - Credentials: creds, - // Creating the FDMap requires that we have kernel.Kernel.fdMapUids, so - // it must wait until we have a Kernel. - Umask: uint(syscall.Umask(0)), + Filename: exec, + Argv: spec.Process.Args, + Envv: spec.Process.Env, + WorkingDirectory: spec.Process.Cwd, + Credentials: creds, + Umask: uint(0022), Limits: ls, MaxSymlinkTraversals: linux.MaxSymlinkTraversals, UTSNamespace: utsns, @@ -240,52 +267,42 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in } ctx := procArgs.NewContext(k) - // Use root user to configure mounts. The current user might not have - // permission to do so. - rootProcArgs := kernel.CreateProcessArgs{ - WorkingDirectory: "/", - Credentials: auth.NewRootCredentials(creds.UserNamespace), - Umask: uint(syscall.Umask(0022)), - MaxSymlinkTraversals: linux.MaxSymlinkTraversals, - } - rootCtx := rootProcArgs.NewContext(k) - - // Create the virtual filesystem. - mns, err := createMountNamespace(ctx, rootCtx, spec, conf, ioFDs) - if err != nil { - return nil, fmt.Errorf("error creating mounts: %v", err) - } - k.SetRootMountNamespace(mns) - - // Create the FD map, which will set stdin, stdout, and stderr. If console - // is true, then ioctl calls will be passed through to the host fd. + // Create the FD map, which will set stdin, stdout, and stderr. If + // console is true, then ioctl calls will be passed through to the host + // fd. fdm, err := createFDMap(ctx, k, ls, console) if err != nil { - return nil, fmt.Errorf("error importing fds: %v", err) + return kernel.CreateProcessArgs{}, fmt.Errorf("error importing fds: %v", err) } // CreateProcess takes a reference on FDMap if successful. We // won't need ours either way. procArgs.FDMap = fdm - // We don't care about child signals; some platforms can generate a - // tremendous number of useless ones (I'm looking at you, ptrace). - if err := sighandling.IgnoreChildStop(); err != nil { - return nil, fmt.Errorf("failed to ignore child stop signals: %v", err) + // If this is the root container, we also need to setup the root mount + // namespace. + if k.RootMountNamespace() == nil { + // Use root user to configure mounts. The current user might not have + // permission to do so. + rootProcArgs := kernel.CreateProcessArgs{ + WorkingDirectory: "/", + Credentials: auth.NewRootCredentials(creds.UserNamespace), + // The sentry should run with a umask of 0. + Umask: uint(syscall.Umask(0)), + MaxSymlinkTraversals: linux.MaxSymlinkTraversals, + } + rootCtx := rootProcArgs.NewContext(k) + + // Create the virtual filesystem. + mns, err := createMountNamespace(ctx, rootCtx, spec, conf, ioFDs) + if err != nil { + return kernel.CreateProcessArgs{}, fmt.Errorf("error creating mounts: %v", err) + } + + k.SetRootMountNamespace(mns) } - // Ensure that most signals received in sentry context are forwarded to - // the emulated kernel. - stopSignalForwarding := sighandling.StartForwarding(k) - return &Loader{ - k: k, - ctrl: ctrl, - conf: conf, - console: console, - watchdog: watchdog, - stopSignalForwarding: stopSignalForwarding, - procArgs: procArgs, - }, nil + return procArgs, nil } // Destroy cleans up all resources used by the loader. @@ -350,17 +367,69 @@ func (l *Loader) run() error { } // Create the root container init task. - if _, err := l.k.CreateProcess(l.procArgs); err != nil { + if _, err := l.k.CreateProcess(l.rootProcArgs); err != nil { return fmt.Errorf("failed to create init process: %v", err) } // CreateProcess takes a reference on FDMap if successful. - l.procArgs.FDMap.DecRef() + l.rootProcArgs.FDMap.DecRef() l.watchdog.Start() return l.k.Start() } +func (l *Loader) startContainer(args *StartArgs, k *kernel.Kernel) error { + spec := args.Spec + // Create capabilities. + caps, err := specutils.Capabilities(spec.Process.Capabilities) + if err != nil { + return fmt.Errorf("error creating capabilities: %v", err) + } + + // Convert the spec's additional GIDs to KGIDs. + extraKGIDs := make([]auth.KGID, 0, len(spec.Process.User.AdditionalGids)) + for _, GID := range spec.Process.User.AdditionalGids { + extraKGIDs = append(extraKGIDs, auth.KGID(GID)) + } + + // Create credentials. We reuse the root user namespace because the + // sentry currently supports only 1 mount namespace, which is tied to a + // single user namespace. Thus we must run in the same user namespace + // to access mounts. + // TODO: Create a new mount namespace for the container. + creds := auth.NewUserCredentials( + auth.KUID(spec.Process.User.UID), + auth.KGID(spec.Process.User.GID), + extraKGIDs, + caps, + l.k.RootUserNamespace()) + + // TODO New containers should be started in new PID namespaces + // when indicated by the spec. + + procArgs, err := newProcess( + args.Spec, + args.Conf, + nil, // ioFDs + false, // console + creds, + k.RootUTSNamespace(), + k.RootIPCNamespace(), + k) + if err != nil { + return fmt.Errorf("failed to create new process: %v", err) + } + + if _, err := l.k.CreateProcess(procArgs); err != nil { + return fmt.Errorf("failed to create process in sentry: %v", err) + } + + // CreateProcess takes a reference on FDMap if successful. + procArgs.FDMap.DecRef() + + return nil +} + // WaitForStartSignal waits for a start signal from the control server. func (l *Loader) WaitForStartSignal() { <-l.ctrl.manager.startChan diff --git a/runsc/cmd/events.go b/runsc/cmd/events.go index f221ad3ae..df65ea31d 100644 --- a/runsc/cmd/events.go +++ b/runsc/cmd/events.go @@ -76,7 +76,7 @@ func (evs *Events) Execute(ctx context.Context, f *flag.FlagSet, args ...interfa c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandox: %v", err) + Fatalf("error loading sandbox: %v", err) } // Repeatedly get stats from the container. diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index 235ed9bc6..cbce07c8e 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -104,7 +104,7 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandox: %v", err) + Fatalf("error loading sandbox: %v", err) } if e.WorkingDirectory == "" { diff --git a/runsc/cmd/ps.go b/runsc/cmd/ps.go index 9f9f4d15e..5d219bfdc 100644 --- a/runsc/cmd/ps.go +++ b/runsc/cmd/ps.go @@ -62,7 +62,7 @@ func (ps *PS) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) c, err := container.Load(conf.RootDir, id) if err != nil { - Fatalf("error loading sandox: %v", err) + Fatalf("error loading sandbox: %v", err) } pList, err := c.Processes() if err != nil { diff --git a/runsc/container/BUILD b/runsc/container/BUILD index fe477abf2..61e05e1c3 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -37,6 +37,7 @@ go_test( "//pkg/sentry/kernel/auth", "//pkg/unet", "//runsc/container", + "//runsc/specutils", "//runsc/test/testutil", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", "@org_golang_x_sys//unix:go_default_library", diff --git a/runsc/container/container.go b/runsc/container/container.go index 571784e07..3b7f95af9 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -214,22 +214,43 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo Owner: os.Getenv("USER"), } - // TODO: If the metadata annotations indicate that this - // container should be started in another sandbox, we must do so. The - // metadata will indicate the ID of the sandbox, which is the same as - // the ID of the init container in the sandbox. We can look up that - // init container by ID to get the sandbox, then we need to expose a - // way to run a new container in the sandbox. - - // Start a new sandbox for this container. Any errors after this point - // must destroy the container. - s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket) - if err != nil { - c.Destroy() - return nil, err - } + // If the metadata annotations indicate that this container should be + // started in an existing sandbox, we must do so. The metadata will + // indicate the ID of the sandbox, which is the same as the ID of the + // init container in the sandbox. + if specutils.ShouldCreateSandbox(spec) { + log.Debugf("Creating new sandbox for container %q", id) + // Start a new sandbox for this container. Any errors after this point + // must destroy the container. + s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket) + if err != nil { + c.Destroy() + return nil, err + } + c.Sandbox = s + } else { + // This is sort of confusing. For a sandbox with a root + // container and a child container in it, runsc sees: + // * A container struct whose sandbox ID is equal to the + // container ID. This is the root container that is tied to + // the creation of the sandbox. + // * A container struct whose sandbox ID is equal to the above + // container/sandbox ID, but that has a different container + // ID. This is the child container. + sbid, ok := specutils.SandboxID(spec) + if !ok { + return nil, fmt.Errorf("no sandbox ID found when creating container") + } + log.Debugf("Creating new container %q in sandbox %q", c.ID, sbid) - c.Sandbox = s + // Find the sandbox associated with this ID. + sb, err := Load(conf.RootDir, sbid) + if err != nil { + c.Destroy() + return nil, err + } + c.Sandbox = sb.Sandbox + } c.Status = Created // Save the metadata file. @@ -242,7 +263,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // this file is created, so it must be the last thing we do. if pidFile != "" { if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(c.Pid())), 0644); err != nil { - s.Destroy() + c.Destroy() return nil, fmt.Errorf("error writing pid file: %v", err) } } @@ -266,9 +287,16 @@ func (c *Container) Start(conf *boot.Config) error { } } - if err := c.Sandbox.Start(c.ID, c.Spec, conf); err != nil { - c.Destroy() - return err + if specutils.ShouldCreateSandbox(c.Spec) { + if err := c.Sandbox.StartRoot(c.Spec, conf); err != nil { + c.Destroy() + return err + } + } else { + if err := c.Sandbox.Start(c.Spec, conf); err != nil { + c.Destroy() + return err + } } // "If any poststart hook fails, the runtime MUST log a warning, but diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 7f87ea5ab..1116ca170 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -36,6 +36,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" "gvisor.googlesource.com/gvisor/pkg/unet" "gvisor.googlesource.com/gvisor/runsc/container" + "gvisor.googlesource.com/gvisor/runsc/specutils" "gvisor.googlesource.com/gvisor/runsc/test/testutil" ) @@ -51,7 +52,7 @@ func waitForProcessList(s *container.Container, expected []*control.Process) err var got []*control.Process for start := time.Now(); time.Now().Sub(start) < 10*time.Second; { var err error - got, err := s.Processes() + got, err = s.Processes() if err != nil { return fmt.Errorf("error getting process data from container: %v", err) } @@ -946,3 +947,73 @@ func TestAbbreviatedIDs(t *testing.T) { } } } + +// TestMultiContainerSanity checks that it is possible to run 2 dead-simple +// containers in the same sandbox. +func TestMultiContainerSanity(t *testing.T) { + containerIDs := []string{ + testutil.UniqueContainerID(), + testutil.UniqueContainerID(), + } + containerAnnotations := []map[string]string{ + // The first container creates a sandbox. + map[string]string{ + specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeSandbox, + }, + // The second container creates a container within the first + // container's sandbox. + map[string]string{ + specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeContainer, + specutils.ContainerdSandboxIDAnnotation: containerIDs[0], + }, + } + + rootDir, err := testutil.SetupRootDir() + if err != nil { + t.Fatalf("error creating root dir: %v", err) + } + defer os.RemoveAll(rootDir) + + // Setup the containers. + containers := make([]*container.Container, 0, len(containerIDs)) + for i, annotations := range containerAnnotations { + spec := testutil.NewSpecWithArgs("sleep", "100") + spec.Annotations = annotations + bundleDir, conf, err := testutil.SetupContainerInRoot(rootDir, spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(bundleDir) + cont, err := container.Create(containerIDs[i], spec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer cont.Destroy() + if err := cont.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + containers = append(containers, cont) + } + + expectedPL := []*control.Process{ + { + UID: 0, + PID: 1, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + { + UID: 0, + PID: 2, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + } + + // Check via ps that multiple processes are running. + if err := waitForProcessList(containers[0], expectedPL); err != nil { + t.Errorf("failed to wait for sleep to start: %v", err) + } +} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 0181dc9d4..90b46e247 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -81,9 +81,9 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo return s, nil } -// Start starts running the containerized process inside the sandbox. -func (s *Sandbox) Start(cid string, spec *specs.Spec, conf *boot.Config) error { - log.Debugf("Start sandbox %q, pid: %d", s.ID, s.Pid) +// StartRoot starts running the root container process inside the sandbox. +func (s *Sandbox) StartRoot(spec *specs.Spec, conf *boot.Config) error { + log.Debugf("Start root sandbox %q, pid: %d", s.ID, s.Pid) conn, err := s.connect() if err != nil { return err @@ -96,9 +96,7 @@ func (s *Sandbox) Start(cid string, spec *specs.Spec, conf *boot.Config) error { } // Send a message to the sandbox control server to start the root - // container.. - // - // TODO: We need a way to start non-root containers. + // container. if err := conn.Call(boot.RootContainerStart, nil, nil); err != nil { return fmt.Errorf("error starting root container %v: %v", spec.Process.Args, err) } @@ -106,6 +104,26 @@ func (s *Sandbox) Start(cid string, spec *specs.Spec, conf *boot.Config) error { return nil } +// Start starts running a non-root container inside the sandbox. +func (s *Sandbox) Start(spec *specs.Spec, conf *boot.Config) error { + log.Debugf("Start non-root container sandbox %q, pid: %d", s.ID, s.Pid) + conn, err := s.connect() + if err != nil { + return err + } + defer conn.Close() + + args := boot.StartArgs{ + Spec: spec, + Conf: conf, + } + if err := conn.Call(boot.ContainerStart, args, nil); err != nil { + return fmt.Errorf("error starting non-root container %v: %v", spec.Process.Args, err) + } + + return nil +} + // Processes retrieves the list of processes and associated metadata for a // given container in this sandbox. func (s *Sandbox) Processes(cid string) ([]*control.Process, error) { @@ -130,11 +148,11 @@ func (s *Sandbox) Execute(cid string, e *control.ExecArgs) (syscall.WaitStatus, log.Debugf("Executing new process in container %q in sandbox %q", cid, s.ID) conn, err := s.connect() if err != nil { - return 0, fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err) + return 0, s.connError(err) } defer conn.Close() - // Send a message to the sandbox control server to start the container.. + // Send a message to the sandbox control server to start the container. var waitStatus uint32 // TODO: Pass in the container id (cid) here. The sandbox // should execute in the context of that container. @@ -168,11 +186,15 @@ func (s *Sandbox) connect() (*urpc.Client, error) { log.Debugf("Connecting to sandbox %q", s.ID) conn, err := client.ConnectTo(boot.ControlSocketAddr(s.ID)) if err != nil { - return nil, fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err) + return nil, s.connError(err) } return conn, nil } +func (s *Sandbox) connError(err error) error { + return fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err) +} + func (s *Sandbox) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir, binPath string) ([]*os.File, error) { if conf.FileAccess != boot.FileAccessProxy { // Don't start a gofer. The sandbox will access host FS directly. @@ -266,7 +288,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } // If the console control socket file is provided, then create a new - // pty master/slave pair and set the tty on the sandox process. + // pty master/slave pair and set the tty on the sandbox process. if consoleEnabled { // setupConsole will send the master on the socket, and return // the slave. diff --git a/runsc/sandbox/sandbox_test.go b/runsc/sandbox/sandbox_test.go index e25290d5e..fee2de283 100644 --- a/runsc/sandbox/sandbox_test.go +++ b/runsc/sandbox/sandbox_test.go @@ -44,7 +44,7 @@ func TestGoferExits(t *testing.T) { t.Fatalf("error creating container: %v", err) } defer s.Destroy() - if err := s.Start("123", spec, conf); err != nil { + if err := s.StartRoot(spec, conf); err != nil { t.Fatalf("error starting container: %v", err) } diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 8dae3efb1..c552111f2 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -63,6 +63,26 @@ func ValidateSpec(spec *specs.Spec) error { if spec.Linux != nil && spec.Linux.Seccomp != nil { log.Warningf("Seccomp spec is being ignored") } + + // 2 annotations are use by containerd to support multi-container pods. + // "io.kubernetes.cri.container-type" + // "io.kubernetes.cri.sandbox-id" + containerType, hasContainerType := spec.Annotations[ContainerdContainerTypeAnnotation] + _, hasSandboxID := spec.Annotations[ContainerdSandboxIDAnnotation] + switch { + // Non-containerd use won't set a container type. + case !hasContainerType: + case containerType == ContainerdContainerTypeSandbox: + // When starting a container in an existing sandbox, the sandbox ID + // must be set. + case containerType == ContainerdContainerTypeContainer: + if !hasSandboxID { + return fmt.Errorf("spec has container-type of %s, but no sandbox ID set", containerType) + } + default: + return fmt.Errorf("unknown container-type: %s", containerType) + } + return nil } @@ -82,7 +102,7 @@ func ReadSpec(bundleDir string) (*specs.Spec, error) { } // GetExecutablePath returns the absolute path to the executable, relative to -// the root. It searches the environment PATH for the first file that exists +// the root. It searches the environment PATH for the first file that exists // with the given name. func GetExecutablePath(exec, root string, env []string) (string, error) { exec = filepath.Clean(exec) @@ -246,6 +266,39 @@ func BinPath() (string, error) { return binPath, nil } +const ( + // ContainerdContainerTypeAnnotation is the OCI annotation set by + // containerd to indicate whether the container to create should have + // its own sandbox or a container within an existing sandbox. + ContainerdContainerTypeAnnotation = "io.kubernetes.cri.container-type" + // ContainerdContainerTypeContainer is the container type value + // indicating the container should be created in an existing sandbox. + ContainerdContainerTypeContainer = "container" + // ContainerdContainerTypeSandbox is the container type value + // indicating the container should be created in a new sandbox. + ContainerdContainerTypeSandbox = "sandbox" + + // ContainerdSandboxIDAnnotation is the OCI annotation set to indicate + // which sandbox the container should be created in when the container + // is not the first container in the sandbox. + ContainerdSandboxIDAnnotation = "io.kubernetes.cri.sandbox-id" +) + +// ShouldCreateSandbox returns true if the spec indicates that a new sandbox +// should be created for the container. If false, the container should be +// started in an existing sandbox. +func ShouldCreateSandbox(spec *specs.Spec) bool { + t, ok := spec.Annotations[ContainerdContainerTypeAnnotation] + return !ok || t == ContainerdContainerTypeSandbox +} + +// SandboxID returns the ID of the sandbox to join and whether an ID was found +// in the spec. +func SandboxID(spec *specs.Spec) (string, bool) { + id, ok := spec.Annotations[ContainerdSandboxIDAnnotation] + return id, ok +} + // WaitForReady waits for a process to become ready. The process is ready when // the 'ready' function returns true. It continues to wait if 'ready' returns // false. It returns error on timeout, if the process stops or if 'ready' fails. -- cgit v1.2.3 From 8459390cdd81ef1c8180948566e893b06233923c Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 28 Jun 2018 09:56:23 -0700 Subject: Error out if spec is invalid Closes #66 PiperOrigin-RevId: 202496258 Change-Id: Ib9287c5bf1279ffba1db21ebd9e6b59305cddf34 --- runsc/boot/loader.go | 2 +- runsc/cmd/boot.go | 6 +- runsc/cmd/cmd.go | 2 +- runsc/cmd/gofer.go | 2 +- runsc/container/container.go | 9 ++- runsc/container/container_test.go | 22 -------- runsc/specutils/BUILD | 1 + runsc/specutils/specutils.go | 27 ++++++++- runsc/specutils/specutils_test.go | 113 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 150 insertions(+), 34 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index da95fa0e7..f359a0eb0 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -267,7 +267,7 @@ func newProcess(spec *specs.Spec, conf *Config, ioFDs []int, console bool, creds Filename: exec, Argv: spec.Process.Args, Envv: spec.Process.Env, - WorkingDirectory: spec.Process.Cwd, + WorkingDirectory: spec.Process.Cwd, // Defaults to '/' if empty. Credentials: creds, Umask: 0022, Limits: ls, diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 0d0e6b63f..685cb6f00 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -23,6 +23,7 @@ import ( "context" "flag" "github.com/google/subcommands" + specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/specutils" @@ -116,6 +117,9 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) if b.applyCaps { caps := spec.Process.Capabilities + if caps == nil { + caps = &specs.LinuxCapabilities{} + } if conf.Platform == boot.PlatformPtrace { // Ptrace platform requires extra capabilities. const c = "CAP_SYS_PTRACE" @@ -131,7 +135,7 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) args = append(args, arg) } } - if err := setCapsAndCallSelf(spec, args, caps); err != nil { + if err := setCapsAndCallSelf(args, caps); err != nil { Fatalf("%v", err) } panic("setCapsAndCallSelf must never return success") diff --git a/runsc/cmd/cmd.go b/runsc/cmd/cmd.go index 940c8cd14..44ebd7165 100644 --- a/runsc/cmd/cmd.go +++ b/runsc/cmd/cmd.go @@ -72,7 +72,7 @@ func (i *intFlags) Set(s string) error { // setCapsAndCallSelf sets capabilities to the current thread and then execve's // itself again with the arguments specified in 'args' to restart the process // with the desired capabilities. -func setCapsAndCallSelf(spec *specs.Spec, args []string, caps *specs.LinuxCapabilities) error { +func setCapsAndCallSelf(args []string, caps *specs.LinuxCapabilities) error { // Keep thread locked while capabilities are changed. runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index 8e1060a35..55315c0e8 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -95,7 +95,7 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) // Note: minimal argument handling for the default case to keep it simple. args := os.Args args = append(args, "--apply-caps=false") - if err := setCapsAndCallSelf(spec, args, lc); err != nil { + if err := setCapsAndCallSelf(args, lc); err != nil { Fatalf("Unable to apply caps: %v", err) } panic("unreachable") diff --git a/runsc/container/container.go b/runsc/container/container.go index 428aa5c62..c7dc6ec10 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -193,9 +193,6 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo if err := validateID(id); err != nil { return nil, err } - if err := specutils.ValidateSpec(spec); err != nil { - return nil, err - } containerRoot := filepath.Join(conf.RootDir, id) if _, err := os.Stat(containerRoot); err == nil { @@ -434,8 +431,10 @@ func (c *Container) Destroy() error { log.Debugf("Destroy container %q", c.ID) // First stop the container. - if err := c.Sandbox.Stop(c.ID); err != nil { - return err + if c.Sandbox != nil { + if err := c.Sandbox.Stop(c.ID); err != nil { + return err + } } // "If any poststop hook fails, the runtime MUST log a warning, but the diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index de487ea97..11285a123 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -812,28 +812,6 @@ func TestConsoleSocket(t *testing.T) { } } -func TestSpecUnsupported(t *testing.T) { - spec := testutil.NewSpecWithArgs("/bin/true") - spec.Process.SelinuxLabel = "somelabel" - - // These are normally set by docker and will just cause warnings to be logged. - spec.Process.ApparmorProfile = "someprofile" - spec.Linux = &specs.Linux{Seccomp: &specs.LinuxSeccomp{}} - - rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) - if err != nil { - t.Fatalf("error setting up container: %v", err) - } - defer os.RemoveAll(rootDir) - defer os.RemoveAll(bundleDir) - - id := testutil.UniqueContainerID() - _, err = container.Create(id, spec, conf, bundleDir, "", "", "") - if err == nil || !strings.Contains(err.Error(), "is not supported") { - t.Errorf("container.Create() wrong error, got: %v, want: *is not supported, spec.Process: %+v", err, spec.Process) - } -} - // TestRunNonRoot checks that sandbox can be configured when running as // non-privileged user. func TestRunNonRoot(t *testing.T) { diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 1b6d265bc..34c952bdf 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -22,4 +22,5 @@ go_test( size = "small", srcs = ["specutils_test.go"], embed = [":specutils"], + deps = ["@com_github_opencontainers_runtime-spec//specs-go:go_default_library"], ) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index c552111f2..0d9e09e9d 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -47,10 +47,28 @@ func LogSpec(spec *specs.Spec) { // ValidateSpec validates that the spec is compatible with runsc. func ValidateSpec(spec *specs.Spec) error { + // Mandatory fields. if spec.Process == nil { - return fmt.Errorf("Process must be defined") + return fmt.Errorf("Spec.Process must be defined: %+v", spec) } - if spec.Process.SelinuxLabel != "" { + if len(spec.Process.Args) == 0 { + return fmt.Errorf("Spec.Process.Arg must be defined: %+v", spec.Process) + } + if spec.Root == nil { + return fmt.Errorf("Spec.Root must be defined: %+v", spec) + } + if len(spec.Root.Path) == 0 { + return fmt.Errorf("Spec.Root.Path must be defined: %+v", spec.Root) + } + + // Unsupported fields. + if spec.Solaris != nil { + return fmt.Errorf("Spec.Solaris is not supported: %+v", spec) + } + if spec.Windows != nil { + return fmt.Errorf("Spec.Windows is not supported: %+v", spec) + } + if len(spec.Process.SelinuxLabel) != 0 { return fmt.Errorf("SELinux is not supported: %s", spec.Process.SelinuxLabel) } @@ -64,7 +82,7 @@ func ValidateSpec(spec *specs.Spec) error { log.Warningf("Seccomp spec is being ignored") } - // 2 annotations are use by containerd to support multi-container pods. + // Two annotations are use by containerd to support multi-container pods. // "io.kubernetes.cri.container-type" // "io.kubernetes.cri.sandbox-id" containerType, hasContainerType := spec.Annotations[ContainerdContainerTypeAnnotation] @@ -98,6 +116,9 @@ func ReadSpec(bundleDir string) (*specs.Spec, error) { if err := json.Unmarshal(specBytes, &spec); err != nil { return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile, err, string(specBytes)) } + if err := ValidateSpec(&spec); err != nil { + return nil, err + } return &spec, nil } diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go index ef293e608..959be3af3 100644 --- a/runsc/specutils/specutils_test.go +++ b/runsc/specutils/specutils_test.go @@ -20,6 +20,8 @@ import ( "strings" "testing" "time" + + specs "github.com/opencontainers/runtime-spec/specs-go" ) func TestWaitForReadyHappy(t *testing.T) { @@ -94,3 +96,114 @@ func TestWaitForReadyTimeout(t *testing.T) { } cmd.Process.Kill() } + +func TestSpecInvalid(t *testing.T) { + for _, test := range []struct { + name string + spec specs.Spec + error string + }{ + { + name: "valid", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + }, + error: "", + }, + { + name: "valid+warning", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + // This is normally set by docker and will just cause warnings to be logged. + ApparmorProfile: "someprofile", + }, + // This is normally set by docker and will just cause warnings to be logged. + Linux: &specs.Linux{Seccomp: &specs.LinuxSeccomp{}}, + }, + error: "", + }, + { + name: "no root", + spec: specs.Spec{ + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + }, + error: "must be defined", + }, + { + name: "empty root", + spec: specs.Spec{ + Root: &specs.Root{}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + }, + error: "must be defined", + }, + { + name: "no process", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + }, + error: "must be defined", + }, + { + name: "empty args", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{}, + }, + error: "must be defined", + }, + { + name: "selinux", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + SelinuxLabel: "somelabel", + }, + }, + error: "is not supported", + }, + { + name: "solaris", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + Solaris: &specs.Solaris{}, + }, + error: "is not supported", + }, + { + name: "windows", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + Windows: &specs.Windows{}, + }, + error: "is not supported", + }, + } { + err := ValidateSpec(&test.spec) + if len(test.error) == 0 { + if err != nil { + t.Errorf("ValidateSpec(%q) failed, err: %v", test.name, err) + } + } else { + if err == nil || !strings.Contains(err.Error(), test.error) { + t.Errorf("ValidateSpec(%q) wrong error, got: %v, want: .*%s.*", test.name, err, test.error) + } + } + } +} -- cgit v1.2.3 From 80bdf8a4068de3ac4a73b6b61a0cdcfe3e3571af Mon Sep 17 00:00:00 2001 From: Justine Olshan Date: Fri, 29 Jun 2018 14:46:45 -0700 Subject: Sets the restore environment for restoring a container. Updated how restoring occurs through boot.go with a separate Restore function. This prevents a new process and new mounts from being created. Added tests to ensure the container is restored. Registered checkpoint and restore commands so they can be used. Docker support for these commands is still limited. Working on #80. PiperOrigin-RevId: 202710950 Change-Id: I2b893ceaef6b9442b1ce3743bd112383cb92af0c --- pkg/sentry/fs/gofer/inode_state.go | 27 ++++++- runsc/boot/fs.go | 103 +++++++++++++++---------- runsc/boot/loader.go | 146 ++++++++++++++++++++++-------------- runsc/boot/loader_test.go | 74 +++++++++++++++++- runsc/cmd/boot.go | 2 + runsc/cmd/checkpoint.go | 2 +- runsc/cmd/restore.go | 2 +- runsc/container/container_test.go | 150 ++++++++++++++++++++++++++++++++++--- runsc/main.go | 2 + runsc/specutils/specutils.go | 11 --- 10 files changed, 391 insertions(+), 128 deletions(-) (limited to 'runsc/specutils') diff --git a/pkg/sentry/fs/gofer/inode_state.go b/pkg/sentry/fs/gofer/inode_state.go index 82d1dd4da..33ec33364 100644 --- a/pkg/sentry/fs/gofer/inode_state.go +++ b/pkg/sentry/fs/gofer/inode_state.go @@ -17,6 +17,7 @@ package gofer import ( "errors" "fmt" + "path/filepath" "strings" "gvisor.googlesource.com/gvisor/pkg/p9" @@ -77,6 +78,29 @@ func (i *inodeFileState) saveLoading() struct{} { return struct{}{} } +// splitAbsolutePath splits the path on slashes ignoring the leading slash. +func splitAbsolutePath(path string) []string { + if len(path) == 0 { + panic("There is no path!") + } + if path != filepath.Clean(path) { + panic(fmt.Sprintf("path %q is not clean", path)) + } + // This case is to return {} rather than {""} + if path == "/" { + return []string{} + } + if path[0] != '/' { + panic(fmt.Sprintf("path %q is not absolute", path)) + } + + s := strings.Split(path, "/") + + // Since p is absolute, the first component of s + // is an empty string. We must remove that. + return s[1:] +} + // loadLoading is invoked by stateify. func (i *inodeFileState) loadLoading(_ struct{}) { i.loading.Lock() @@ -98,7 +122,8 @@ func (i *inodeFileState) afterLoad() { // TODO: Context is not plumbed to save/restore. ctx := &dummyClockContext{context.Background()} var err error - _, i.file, err = i.s.attach.walk(ctx, strings.Split(name, "/")) + + _, i.file, err = i.s.attach.walk(ctx, splitAbsolutePath(name)) if err != nil { return fmt.Errorf("failed to walk to %q: %v", name, err) } diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index e0d7fc769..a9b2f225a 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -38,6 +38,14 @@ import ( "gvisor.googlesource.com/gvisor/runsc/specutils" ) +const ( + // Filesystem name for 9p gofer mounts. + rootFsName = "9p" + + // Device name for root mount. + rootDevice = "9pfs-/" +) + type fdDispenser struct { fds []int } @@ -64,7 +72,8 @@ func createMountNamespace(userCtx context.Context, rootCtx context.Context, spec if err != nil { return nil, fmt.Errorf("failed to create root mount namespace: %v", err) } - if err := configureMounts(rootCtx, spec, conf, mns, fds); err != nil { + mounts := compileMounts(spec) + if err := setMounts(rootCtx, conf, mns, fds, mounts); err != nil { return nil, fmt.Errorf("failed to configure mounts: %v", err) } if !fds.empty() { @@ -73,27 +82,23 @@ func createMountNamespace(userCtx context.Context, rootCtx context.Context, spec return mns, nil } -// configureMounts iterates over Spec.Mounts and mounts them in the specified -// mount namespace. -func configureMounts(ctx context.Context, spec *specs.Spec, conf *Config, mns *fs.MountNamespace, fds *fdDispenser) error { +// compileMounts returns the supported mounts from the mount spec, adding any +// additional mounts that are required by the OCI specification. +func compileMounts(spec *specs.Spec) []specs.Mount { // Keep track of whether proc, sys, and tmp were mounted. var procMounted, sysMounted, tmpMounted bool + var mounts []specs.Mount // Always mount /dev. - if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ + mounts = append(mounts, specs.Mount{ Type: "devtmpfs", Destination: "/dev", - }); err != nil { - return err - } + }) - // Always mount /dev/pts. - if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ + mounts = append(mounts, specs.Mount{ Type: "devpts", Destination: "/dev/pts", - }); err != nil { - return err - } + }) // Mount all submounts from the spec. for _, m := range spec.Mounts { @@ -101,6 +106,7 @@ func configureMounts(ctx context.Context, spec *specs.Spec, conf *Config, mns *f log.Warningf("ignoring dev mount at %q", m.Destination) continue } + mounts = append(mounts, m) switch filepath.Clean(m.Destination) { case "/proc": procMounted = true @@ -109,43 +115,45 @@ func configureMounts(ctx context.Context, spec *specs.Spec, conf *Config, mns *f case "/tmp": tmpMounted = true } - - if err := mountSubmount(ctx, spec, conf, mns, fds, m); err != nil { - return err - } } // Mount proc and sys even if the user did not ask for it, as the spec // says we SHOULD. if !procMounted { - if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ + mounts = append(mounts, specs.Mount{ Type: "proc", Destination: "/proc", - }); err != nil { - return err - } + }) } if !sysMounted { - if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ + mounts = append(mounts, specs.Mount{ Type: "sysfs", Destination: "/sys", - }); err != nil { - return err - } + }) } // Technically we don't have to mount tmpfs at /tmp, as we could just // rely on the host /tmp, but this is a nice optimization, and fixes // some apps that call mknod in /tmp. if !tmpMounted { - if err := mountSubmount(ctx, spec, conf, mns, nil, specs.Mount{ + mounts = append(mounts, specs.Mount{ Type: "tmpfs", Destination: "/tmp", - }); err != nil { + }) + } + return mounts +} + +// setMounts iterates over mounts and mounts them in the specified +// mount namespace. +func setMounts(ctx context.Context, conf *Config, mns *fs.MountNamespace, fds *fdDispenser, mounts []specs.Mount) error { + + // Mount all submounts from mounts. + for _, m := range mounts { + if err := mountSubmount(ctx, conf, mns, fds, m, mounts); err != nil { return err } } - return nil } @@ -158,19 +166,20 @@ func createRootMount(ctx context.Context, spec *specs.Spec, conf *Config, fds *f rootInode *fs.Inode err error ) + switch conf.FileAccess { case FileAccessProxy: fd := fds.remove() log.Infof("Mounting root over 9P, ioFD: %d", fd) hostFS := mustFindFilesystem("9p") - rootInode, err = hostFS.Mount(ctx, "root", mf, fmt.Sprintf("trans=fd,rfdno=%d,wfdno=%d,privateunixsocket=true", fd, fd)) + rootInode, err = hostFS.Mount(ctx, rootDevice, mf, fmt.Sprintf("trans=fd,rfdno=%d,wfdno=%d,privateunixsocket=true", fd, fd)) if err != nil { return nil, fmt.Errorf("failed to generate root mount point: %v", err) } case FileAccessDirect: hostFS := mustFindFilesystem("whitelistfs") - rootInode, err = hostFS.Mount(ctx, "root", mf, "root="+spec.Root.Path+",dont_translate_ownership=true") + rootInode, err = hostFS.Mount(ctx, rootDevice, mf, "root="+spec.Root.Path+",dont_translate_ownership=true") if err != nil { return nil, fmt.Errorf("failed to generate root mount point: %v", err) } @@ -263,7 +272,7 @@ func getMountNameAndOptions(conf *Config, m specs.Mount, fds *fdDispenser) (stri return fsName, data, useOverlay, err } -func mountSubmount(ctx context.Context, spec *specs.Spec, conf *Config, mns *fs.MountNamespace, fds *fdDispenser, m specs.Mount) error { +func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, fds *fdDispenser, m specs.Mount, mounts []specs.Mount) error { // Map mount type to filesystem name, and parse out the options that we are // capable of dealing with. fsName, data, useOverlay, err := getMountNameAndOptions(conf, m, fds) @@ -285,14 +294,13 @@ func mountSubmount(ctx context.Context, spec *specs.Spec, conf *Config, mns *fs. mf.ReadOnly = true } - inode, err := filesystem.Mount(ctx, m.Type, mf, strings.Join(data, ",")) + inode, err := filesystem.Mount(ctx, mountDevice(m), mf, strings.Join(data, ",")) if err != nil { return fmt.Errorf("failed to create mount with source %q: %v", m.Source, err) } // If there are submounts, we need to overlay the mount on top of a // ramfs with stub directories for submount paths. - mounts := specutils.SupportedMounts(spec.Mounts) submounts := subtargets(m.Destination, mounts) if len(submounts) > 0 { log.Infof("Adding submount overlay over %q", m.Destination) @@ -406,7 +414,7 @@ func mountDevice(m specs.Mount) string { if m.Type == "bind" { // Make a device string that includes the target, which is consistent across // S/R and uniquely identifies the connection. - return "p9fs-" + m.Destination + return "9pfs-" + m.Destination } // All other fs types use device "none". return "none" @@ -417,14 +425,24 @@ func mountDevice(m specs.Mount) string { func addRestoreMount(conf *Config, renv *fs.RestoreEnvironment, m specs.Mount, fds *fdDispenser) error { fsName, data, _, err := getMountNameAndOptions(conf, m, fds) dataString := strings.Join(data, ",") + + // Return the error or nil that corresponds to the default case in getMountNameAndOptions. if err != nil { return err } - renv.MountSources[fsName] = append(renv.MountSources[fsName], fs.MountArgs{ + // TODO: Fix this when we support all the mount types and make this a + // fatal error. + if fsName == "" { + return nil + } + + newMount := fs.MountArgs{ Dev: mountDevice(m), Flags: mountFlags(m.Options), Data: dataString, - }) + } + renv.MountSources[fsName] = append(renv.MountSources[fsName], newMount) + log.Infof("Added mount at %q: %+v", fsName, newMount) return nil } @@ -438,6 +456,8 @@ func createRestoreEnvironment(spec *specs.Spec, conf *Config, fds *fdDispenser) MountSources: make(map[string][]fs.MountArgs), } + mounts := compileMounts(spec) + // Add root mount. fd := fds.remove() dataString := strings.Join([]string{"trans=fd", fmt.Sprintf("rfdno=%d", fd), fmt.Sprintf("wfdno=%d", fd), "privateunixsocket=true"}, ",") @@ -445,15 +465,16 @@ func createRestoreEnvironment(spec *specs.Spec, conf *Config, fds *fdDispenser) if spec.Root.Readonly { mf.ReadOnly = true } - const rootFSName = "9p" - renv.MountSources[rootFSName] = append(renv.MountSources[rootFSName], fs.MountArgs{ - Dev: "p9fs-/", + + rootMount := fs.MountArgs{ + Dev: rootDevice, Flags: mf, Data: dataString, - }) + } + renv.MountSources[rootFsName] = append(renv.MountSources[rootFsName], rootMount) // Add submounts - for _, m := range spec.Mounts { + for _, m := range mounts { if err := addRestoreMount(conf, renv, m, fds); err != nil { return nil, err } diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 014908179..6fcfba5cb 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -29,6 +29,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/cpuid" "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/inet" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" @@ -80,6 +81,9 @@ type Loader struct { // container. It should be called when a sandbox is destroyed. stopSignalForwarding func() + // restore is set to true if we are restoring a container. + restore bool + // rootProcArgs refers to the root sandbox init task. rootProcArgs kernel.CreateProcessArgs @@ -106,7 +110,17 @@ func init() { } // New initializes a new kernel loader configured by spec. +// New also handles setting up a kernel for restoring a container. func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []int, console bool) (*Loader, error) { + var ( + tk *kernel.Timekeeper + creds *auth.Credentials + vdso *loader.VDSO + utsns *kernel.UTSNamespace + ipcns *kernel.IPCNamespace + restoreFile *os.File + procArgs kernel.CreateProcessArgs + ) // Create kernel and platform. p, err := createPlatform(conf) if err != nil { @@ -116,47 +130,60 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in Platform: p, } - // Create VDSO. - // - // Pass k as the platform since it is savable, unlike the actual platform. - vdso, err := loader.PrepareVDSO(k) - if err != nil { - return nil, fmt.Errorf("error creating vdso: %v", err) - } + if restoreFD == -1 { + // Create VDSO. + // + // Pass k as the platform since it is savable, unlike the actual platform. + vdso, err := loader.PrepareVDSO(k) + if err != nil { + return nil, fmt.Errorf("error creating vdso: %v", err) + } - // Create timekeeper. - tk, err := kernel.NewTimekeeper(k, vdso.ParamPage.FileRange()) - if err != nil { - return nil, fmt.Errorf("error creating timekeeper: %v", err) - } - tk.SetClocks(time.NewCalibratedClocks()) + // Create timekeeper. + tk, err = kernel.NewTimekeeper(k, vdso.ParamPage.FileRange()) + if err != nil { + return nil, fmt.Errorf("error creating timekeeper: %v", err) + } + tk.SetClocks(time.NewCalibratedClocks()) - // Create capabilities. - caps, err := specutils.Capabilities(spec.Process.Capabilities) - if err != nil { - return nil, fmt.Errorf("error creating capabilities: %v", err) - } + // Create capabilities. + caps, err := specutils.Capabilities(spec.Process.Capabilities) + if err != nil { + return nil, fmt.Errorf("error creating capabilities: %v", err) + } - // Convert the spec's additional GIDs to KGIDs. - extraKGIDs := make([]auth.KGID, 0, len(spec.Process.User.AdditionalGids)) - for _, GID := range spec.Process.User.AdditionalGids { - extraKGIDs = append(extraKGIDs, auth.KGID(GID)) - } + // Convert the spec's additional GIDs to KGIDs. + extraKGIDs := make([]auth.KGID, 0, len(spec.Process.User.AdditionalGids)) + for _, GID := range spec.Process.User.AdditionalGids { + extraKGIDs = append(extraKGIDs, auth.KGID(GID)) + } - // Create credentials. - creds := auth.NewUserCredentials( - auth.KUID(spec.Process.User.UID), - auth.KGID(spec.Process.User.GID), - extraKGIDs, - caps, - auth.NewRootUserNamespace()) + // Create credentials. + creds = auth.NewUserCredentials( + auth.KUID(spec.Process.User.UID), + auth.KGID(spec.Process.User.GID), + extraKGIDs, + caps, + auth.NewRootUserNamespace()) - // Create user namespace. - // TODO: Not clear what domain name should be here. It is - // not configurable from runtime spec. - utsns := kernel.NewUTSNamespace(spec.Hostname, "", creds.UserNamespace) + // Create user namespace. + // TODO: Not clear what domain name should be here. It is + // not configurable from runtime spec. + utsns = kernel.NewUTSNamespace(spec.Hostname, "", creds.UserNamespace) - ipcns := kernel.NewIPCNamespace(creds.UserNamespace) + ipcns = kernel.NewIPCNamespace(creds.UserNamespace) + } else { + // Create and set RestoreEnvironment + fds := &fdDispenser{fds: ioFDs} + renv, err := createRestoreEnvironment(spec, conf, fds) + if err != nil { + return nil, fmt.Errorf("error creating RestoreEnvironment: %v", err) + } + fs.SetRestoreEnvironment(*renv) + + restoreFile = os.NewFile(uintptr(restoreFD), "restore_file") + defer restoreFile.Close() + } if err := enableStrace(conf); err != nil { return nil, fmt.Errorf("failed to enable strace: %v", err) @@ -168,19 +195,7 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in // Run(). networkStack := newEmptyNetworkStack(conf, k) - // Check if we need to restore the kernel - if restoreFD != -1 { - restoreFile := os.NewFile(uintptr(restoreFD), "restore_file") - defer restoreFile.Close() - - // Load the state. - loadOpts := state.LoadOpts{ - Source: restoreFile, - } - if err := loadOpts.Load(k, p, networkStack); err != nil { - return nil, err - } - } else { + if restoreFile == nil { // Initiate the Kernel object, which is required by the Context passed // to createVFS in order to mount (among other things) procfs. if err = k.Init(kernel.InitKernelArgs{ @@ -196,6 +211,17 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in }); err != nil { return nil, fmt.Errorf("error initializing kernel: %v", err) } + } else { + // Load the state. + loadOpts := state.LoadOpts{ + Source: restoreFile, + } + if err := loadOpts.Load(k, p, networkStack); err != nil { + return nil, err + } + + // Set timekeeper. + k.Timekeeper().SetClocks(time.NewCalibratedClocks()) } // Turn on packet logging if enabled. @@ -232,9 +258,11 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in // Ensure that signals received are forwarded to the emulated kernel. stopSignalForwarding := sighandling.PrepareForwarding(k, false)() - procArgs, err := newProcess(spec, conf, ioFDs, console, creds, utsns, ipcns, k) - if err != nil { - return nil, fmt.Errorf("failed to create root process: %v", err) + if restoreFile == nil { + procArgs, err = newProcess(spec, conf, ioFDs, console, creds, utsns, ipcns, k) + if err != nil { + return nil, fmt.Errorf("failed to create root process: %v", err) + } } l := &Loader{ @@ -245,6 +273,7 @@ func New(spec *specs.Spec, conf *Config, controllerFD, restoreFD int, ioFDs []in watchdog: watchdog, stopSignalForwarding: stopSignalForwarding, rootProcArgs: procArgs, + restore: restoreFile != nil, } ctrl.manager.l = l return l, nil @@ -378,13 +407,16 @@ func (l *Loader) run() error { } } - // Create the root container init task. - if _, err := l.k.CreateProcess(l.rootProcArgs); err != nil { - return fmt.Errorf("failed to create init process: %v", err) - } + // If we are restoring, we do not want to create a process. + if !l.restore { + // Create the root container init task. + if _, err := l.k.CreateProcess(l.rootProcArgs); err != nil { + return fmt.Errorf("failed to create init process: %v", err) + } - // CreateProcess takes a reference on FDMap if successful. - l.rootProcArgs.FDMap.DecRef() + // CreateProcess takes a reference on FDMap if successful. + l.rootProcArgs.FDMap.DecRef() + } l.watchdog.Start() return l.k.Start() diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index 15ced0601..28d45b54b 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -364,7 +364,7 @@ func TestRestoreEnvironment(t *testing.T) { MountSources: map[string][]fs.MountArgs{ "9p": { { - Dev: "p9fs-/", + Dev: "9pfs-/", Flags: fs.MountSourceFlags{ReadOnly: true}, Data: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true", }, @@ -376,6 +376,24 @@ func TestRestoreEnvironment(t *testing.T) { { Dev: "none", }, + { + Dev: "none", + }, + }, + "devtmpfs": { + { + Dev: "none", + }, + }, + "devpts": { + { + Dev: "none", + }, + }, + "sysfs": { + { + Dev: "none", + }, }, }, }, @@ -406,15 +424,40 @@ func TestRestoreEnvironment(t *testing.T) { MountSources: map[string][]fs.MountArgs{ "9p": { { - Dev: "p9fs-/", + Dev: "9pfs-/", Flags: fs.MountSourceFlags{ReadOnly: true}, Data: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true", }, { - Dev: "p9fs-/dev/fd-foo", + Dev: "9pfs-/dev/fd-foo", Data: "trans=fd,rfdno=1,wfdno=1,privateunixsocket=true", }, }, + "tmpfs": { + { + Dev: "none", + }, + }, + "devtmpfs": { + { + Dev: "none", + }, + }, + "devpts": { + { + Dev: "none", + }, + }, + "proc": { + { + Dev: "none", + }, + }, + "sysfs": { + { + Dev: "none", + }, + }, }, }, }, @@ -445,7 +488,7 @@ func TestRestoreEnvironment(t *testing.T) { MountSources: map[string][]fs.MountArgs{ "9p": { { - Dev: "p9fs-/", + Dev: "9pfs-/", Flags: fs.MountSourceFlags{ReadOnly: true}, Data: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true", }, @@ -456,6 +499,29 @@ func TestRestoreEnvironment(t *testing.T) { Flags: fs.MountSourceFlags{NoAtime: true}, Data: "uid=1022", }, + { + Dev: "none", + }, + }, + "devtmpfs": { + { + Dev: "none", + }, + }, + "devpts": { + { + Dev: "none", + }, + }, + "proc": { + { + Dev: "none", + }, + }, + "sysfs": { + { + Dev: "none", + }, }, }, }, diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 685cb6f00..b19da315f 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -142,7 +142,9 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } // Create the loader. + l, err := boot.New(spec, conf, b.controllerFD, b.restoreFD, b.ioFDs.GetArray(), b.console) + if err != nil { Fatalf("error creating loader: %v", err) } diff --git a/runsc/cmd/checkpoint.go b/runsc/cmd/checkpoint.go index 9348289ca..94efc3517 100644 --- a/runsc/cmd/checkpoint.go +++ b/runsc/cmd/checkpoint.go @@ -44,7 +44,7 @@ func (*Checkpoint) Name() string { // Synopsis implements subcommands.Command.Synopsis. func (*Checkpoint) Synopsis() string { - return "checkpoint current state of container" + return "checkpoint current state of container (experimental)" } // Usage implements subcommands.Command.Usage. diff --git a/runsc/cmd/restore.go b/runsc/cmd/restore.go index cc55beeaf..69cdb35c1 100644 --- a/runsc/cmd/restore.go +++ b/runsc/cmd/restore.go @@ -42,7 +42,7 @@ func (*Restore) Name() string { // Synopsis implements subcommands.Command.Synopsis. func (*Restore) Synopsis() string { - return "restore a saved state of container" + return "restore a saved state of container (experimental)" } // Usage implements subcommands.Command.Usage. diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index ae500e7d0..a6bb39c5d 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -22,6 +22,7 @@ import ( "path" "path/filepath" "reflect" + "strconv" "strings" "sync" "syscall" @@ -106,6 +107,56 @@ func procListToString(pl []*control.Process) string { return fmt.Sprintf("[%s]", strings.Join(strs, ",")) } +// createWriteableOutputFile creates an output file that can be read and written to in the sandbox. +func createWriteableOutputFile(path string) (*os.File, error) { + outputFile, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666) + if err != nil { + return nil, fmt.Errorf("error creating file: %q, %v", path, err) + } + + // Chmod to allow writing after umask. + if err := outputFile.Chmod(0666); err != nil { + return nil, fmt.Errorf("error chmoding file: %q, %v", path, err) + } + return outputFile, nil +} + +func readOutputNum(outputFile *os.File, path string, first bool) (int, error) { + var num int + time.Sleep(1 * time.Second) + + // Check that outputFile exists and contains counting data. + fileInfo, err := os.Stat(path) + if err != nil { + return 0, fmt.Errorf("error creating output file: %v", err) + } + + if fileInfo.Size() == 0 { + return 0, fmt.Errorf("failed to write to file, file still appears empty") + } + + // Read the first number in the new file + outputFileContent, err := ioutil.ReadAll(outputFile) + if err != nil { + return 0, fmt.Errorf("error reading file: %v", err) + } + if len(outputFileContent) == 0 { + return 0, fmt.Errorf("error no content was read") + } + + nums := strings.Split(string(outputFileContent), "\n") + + if first { + num, err = strconv.Atoi(nums[0]) + } else { + num, err = strconv.Atoi(nums[len(nums)-2]) + } + if err != nil { + return 0, fmt.Errorf("error getting number from file: %v", err) + } + return num, nil +} + // run starts the sandbox and waits for it to exit, checking that the // application succeeded. func run(spec *specs.Spec) error { @@ -429,13 +480,28 @@ func TestExec(t *testing.T) { } } -// TestCheckpoint verifies that calling checkpoint with an image-path flag succeeds. -// Since there is no current default image path, confirming that calling -// checkpoint without an image path fails. -// Checks that there is a file with the name and location given by image path. -func TestCheckpoint(t *testing.T) { - // Container will succeed. - spec := testutil.NewSpecWithArgs("sleep", "100") +// TestCheckpointRestore creates a container that continuously writes successive integers +// to a file. To test checkpoint and restore functionality, the container is +// checkpointed and the last number printed to the file is recorded. Then, it is restored in two +// new containers and the first number printed from these containers is checked. Both should +// be the next consecutive number after the last number from the checkpointed container. +func TestCheckpointRestore(t *testing.T) { + outputPath := filepath.Join(os.TempDir(), "output") + outputFile, err := createWriteableOutputFile(outputPath) + if err != nil { + t.Fatalf("error creating output file: %v", err) + } + defer outputFile.Close() + + outputFileSandbox := strings.Replace(outputPath, os.TempDir(), "/tmp2", -1) + + script := fmt.Sprintf("for ((i=0; ;i++)); do echo $i >> %s; sleep 1; done", outputFileSandbox) + spec := testutil.NewSpecWithArgs("bash", "-c", script) + spec.Mounts = append(spec.Mounts, specs.Mount{ + Type: "bind", + Destination: "/tmp2", + Source: os.TempDir(), + }) rootDir, bundleDir, conf, err := testutil.SetupContainer(spec) if err != nil { @@ -464,20 +530,80 @@ func TestCheckpoint(t *testing.T) { } defer file.Close() + time.Sleep(1 * time.Second) + // Checkpoint running container; save state into new file. if err := cont.Checkpoint(file); err != nil { t.Fatalf("error checkpointing container to empty file: %v", err) } defer os.RemoveAll(imagePath) - // Check to see if file exists and contains data. - fileInfo, err := os.Stat(imagePath) + lastNum, err := readOutputNum(outputFile, outputPath, false) + if err != nil { + t.Fatalf("error with outputFile: %v", err) + } + + // Delete and recreate file before restoring. + if err := os.Remove(outputPath); err != nil { + t.Fatalf("error removing file") + } + outputFile2, err := createWriteableOutputFile(outputPath) + if err != nil { + t.Fatalf("error creating output file: %v", err) + } + defer outputFile2.Close() + + // Restore into a new container. + cont2, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", imagePath) if err != nil { - t.Fatalf("error checkpointing container: %v", err) + t.Fatalf("error creating container: %v", err) + } + defer cont2.Destroy() + if err := cont2.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + + firstNum, err := readOutputNum(outputFile2, outputPath, true) + if err != nil { + t.Fatalf("error with outputFile: %v", err) } - if size := fileInfo.Size(); size == 0 { - t.Fatalf("failed checkpoint, file still appears empty: %v", err) + + // Check that lastNum is one less than firstNum and that the container picks up from where it left off. + if lastNum+1 != firstNum { + t.Errorf("error numbers not in order, previous: %d, next: %d", lastNum, firstNum) } + + // Restore into another container! + // Delete and recreate file before restoring. + if err := os.Remove(outputPath); err != nil { + t.Fatalf("error removing file") + } + outputFile3, err := createWriteableOutputFile(outputPath) + if err != nil { + t.Fatalf("error creating output file: %v", err) + } + defer outputFile3.Close() + + // Restore into a new container. + cont3, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", imagePath) + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer cont3.Destroy() + if err := cont3.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + + firstNum2, err := readOutputNum(outputFile3, outputPath, true) + if err != nil { + t.Fatalf("error with outputFile: %v", err) + } + + // Check that lastNum is one less than firstNum and that the container picks up from where it left off. + if lastNum+1 != firstNum2 { + t.Errorf("error numbers not in order, previous: %d, next: %d", lastNum, firstNum) + } + } // TestPauseResume tests that we can successfully pause and resume a container. diff --git a/runsc/main.go b/runsc/main.go index dfb338b0f..10ae44b5e 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -71,6 +71,7 @@ func main() { subcommands.Register(subcommands.FlagsCommand(), "") // Register user-facing runsc commands. + subcommands.Register(new(cmd.Checkpoint), "") subcommands.Register(new(cmd.Create), "") subcommands.Register(new(cmd.Delete), "") subcommands.Register(new(cmd.Events), "") @@ -80,6 +81,7 @@ func main() { subcommands.Register(new(cmd.List), "") subcommands.Register(new(cmd.Pause), "") subcommands.Register(new(cmd.PS), "") + subcommands.Register(new(cmd.Restore), "") subcommands.Register(new(cmd.Resume), "") subcommands.Register(new(cmd.Run), "") subcommands.Register(new(cmd.Start), "") diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 0d9e09e9d..34243e623 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -266,17 +266,6 @@ func IsSupportedDevMount(m specs.Mount) bool { return true } -// SupportedMounts filters out unsupported mounts. -func SupportedMounts(mounts []specs.Mount) []specs.Mount { - var newMounts []specs.Mount - for _, m := range mounts { - if IsSupportedDevMount(m) { - newMounts = append(newMounts, m) - } - } - return newMounts -} - // BinPath returns the real path to self, resolving symbolink links. This is done // to make the process name appears as 'runsc', instead of 'exe'. func BinPath() (string, error) { -- cgit v1.2.3 From b763b3992a2c4f16fc218e1920df5525dd75b114 Mon Sep 17 00:00:00 2001 From: Brielle Broder Date: Tue, 10 Jul 2018 14:57:20 -0700 Subject: Modified error message for clarity. Previously, error message only showed "" when child and pid were the same (since no error is returned by the Wait4 syscall in this case) which occurs when the process has incorrectly terminated. A new error message was added to improve clarity for such a case. Tests for this function were modified to reflect the improved distinction between process termination and error. PiperOrigin-RevId: 204018107 Change-Id: Ib38481c9590405e5bafcb6efe27fd49b3948910c --- runsc/specutils/specutils.go | 9 +++++++-- runsc/specutils/specutils_test.go | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 34243e623..861e7fd70 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -324,9 +324,14 @@ func WaitForReady(pid int, timeout time.Duration, ready func() (bool, error)) er // Check if the process is still running. var ws syscall.WaitStatus var ru syscall.Rusage + + // If the process is alive, child is 0 because of the NOHANG option. + // If the process has terminated, child equals the process id. child, err := syscall.Wait4(pid, &ws, syscall.WNOHANG, &ru) - if err != nil || child == pid { - return fmt.Errorf("process (%d) is not running, err: %v", pid, err) + if err != nil { + return fmt.Errorf("error waiting for process: %v", err) + } else if child == pid { + return fmt.Errorf("process %d has terminated", pid) } // Process continues to run, backoff and retry. diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go index 959be3af3..2dc5d90cc 100644 --- a/runsc/specutils/specutils_test.go +++ b/runsc/specutils/specutils_test.go @@ -76,8 +76,11 @@ func TestWaitForReadyNotRunning(t *testing.T) { err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) { return false, nil }) - if !strings.Contains(err.Error(), "not running") { - t.Errorf("ProcessWaitReady got: %v, expected: not running", err) + if err != nil && !strings.Contains(err.Error(), "terminated") { + t.Errorf("ProcessWaitReady got: %v, expected: process terminated", err) + } + if err == nil { + t.Errorf("ProcessWaitReady incorrectly succeeded") } } -- cgit v1.2.3 From 413bfb39a940455cb116c7d0ca715b2ced78a11c Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Tue, 31 Jul 2018 15:06:36 -0700 Subject: Use backoff package for retry logic PiperOrigin-RevId: 206834838 Change-Id: I9a44c6fa5f4766a01f86e90810f025cefecdf2d4 --- runsc/specutils/BUILD | 1 + runsc/specutils/specutils.go | 30 ++++++++++++++---------------- runsc/specutils/specutils_test.go | 4 ++-- 3 files changed, 17 insertions(+), 18 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 34c952bdf..a22ab789a 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -13,6 +13,7 @@ go_library( "//pkg/abi/linux", "//pkg/log", "//pkg/sentry/kernel/auth", + "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", ], ) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 861e7fd70..27441cbde 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -26,6 +26,7 @@ import ( "syscall" "time" + "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/log" @@ -313,33 +314,30 @@ func SandboxID(spec *specs.Spec) (string, bool) { // the 'ready' function returns true. It continues to wait if 'ready' returns // false. It returns error on timeout, if the process stops or if 'ready' fails. func WaitForReady(pid int, timeout time.Duration, ready func() (bool, error)) error { - backoff := 1 * time.Millisecond - for start := time.Now(); time.Now().Sub(start) < timeout; { + b := backoff.NewExponentialBackOff() + b.InitialInterval = 1 * time.Millisecond + b.MaxInterval = 1 * time.Second + b.MaxElapsedTime = timeout + + op := func() error { if ok, err := ready(); err != nil { - return err + return backoff.Permanent(err) } else if ok { return nil } // Check if the process is still running. - var ws syscall.WaitStatus - var ru syscall.Rusage - // If the process is alive, child is 0 because of the NOHANG option. // If the process has terminated, child equals the process id. + var ws syscall.WaitStatus + var ru syscall.Rusage child, err := syscall.Wait4(pid, &ws, syscall.WNOHANG, &ru) if err != nil { - return fmt.Errorf("error waiting for process: %v", err) + return backoff.Permanent(fmt.Errorf("error waiting for process: %v", err)) } else if child == pid { - return fmt.Errorf("process %d has terminated", pid) - } - - // Process continues to run, backoff and retry. - time.Sleep(backoff) - backoff *= 2 - if backoff > 1*time.Second { - backoff = 1 * time.Second + return backoff.Permanent(fmt.Errorf("process %d has terminated", pid)) } + return fmt.Errorf("process %d not running yet", pid) } - return fmt.Errorf("timed out waiting for process (%d)", pid) + return backoff.Retry(op, b) } diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go index 2dc5d90cc..2c4e3e729 100644 --- a/runsc/specutils/specutils_test.go +++ b/runsc/specutils/specutils_test.go @@ -94,8 +94,8 @@ func TestWaitForReadyTimeout(t *testing.T) { err := WaitForReady(cmd.Process.Pid, 50*time.Millisecond, func() (bool, error) { return false, nil }) - if !strings.Contains(err.Error(), "timed out") { - t.Errorf("ProcessWaitReady got: %v, expected: timed out", err) + if !strings.Contains(err.Error(), "not running yet") { + t.Errorf("ProcessWaitReady got: %v, expected: not running yet", err) } cmd.Process.Kill() } -- cgit v1.2.3 From 02dfceab6d4c4a2a3342ef69be0265b7ab03e5d7 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 24 Aug 2018 14:41:38 -0700 Subject: runsc: Allow runsc to properly search the PATH for executable name. Previously, runsc improperly attempted to find an executable in the container's PATH. We now search the PATH via the container's fsgofer rather than the host FS, eliminating the confusing differences between paths on the host and within a container. PiperOrigin-RevId: 210159488 Change-Id: I228174dbebc4c5356599036d6efaa59f28ff28d2 --- runsc/boot/fs.go | 45 ++++++++++++++++++++++++++++++++++++++++++++ runsc/boot/loader.go | 4 ++++ runsc/specutils/specutils.go | 25 ++++++++++++++---------- 3 files changed, 64 insertions(+), 10 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 8996b1398..6f5379a6d 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -16,6 +16,7 @@ package boot import ( "fmt" + "path" "path/filepath" "strconv" "strings" @@ -701,3 +702,47 @@ func setFileSystemForProcess(procArgs *kernel.CreateProcessArgs, spec *specs.Spe procArgs.Root = containerRootDirent return nil } + +// GetExecutablePathInternal traverses the *container's* filesystem to resolve +// exec's absolute path. For example, if the container is being served files by +// the fsgofer serving /foo/bar as the container root, it will search within +// /foo/bar, not the host root. +// TODO: Unit test this. +func GetExecutablePathInternal(ctx context.Context, procArgs *kernel.CreateProcessArgs) (string, error) { + exec := filepath.Clean(procArgs.Filename) + + // Don't search PATH if exec is a path to a file (absolute or relative). + if strings.IndexByte(exec, '/') >= 0 { + return exec, nil + } + + // Search the PATH for a file whose name matches the one we are looking + // for. + pathDirs := specutils.GetPath(procArgs.Envv) + for _, p := range pathDirs { + // Walk to the end of the path. + curDir := procArgs.Root + for _, pc := range strings.Split(p, "/") { + var err error + if curDir, err = curDir.Walk(ctx, curDir, pc); err != nil { + break + } + } + if curDir == nil { + continue + } + // Check for the executable in the path directory. + dirent, err := curDir.Walk(ctx, curDir, exec) + if err != nil { + continue + } + // Check whether we can read and execute the file in question. + if err := dirent.Inode.CheckPermission(ctx, fs.PermMask{Read: true, Execute: true}); err != nil { + log.Infof("Found executable at %q, but user cannot execute it: %v", path.Join(p, exec), err) + continue + } + return path.Join("/", p, exec), nil + } + + return "", fmt.Errorf("could not find executable %s in path %v", exec, pathDirs) +} diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 7debf0ac2..2f212c704 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -428,6 +428,10 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config return 0, fmt.Errorf("failed to create new process: %v", err) } + if procArgs.Filename, err = GetExecutablePathInternal(procArgs.NewContext(k), &procArgs); err != nil { + return 0, err + } + tg, err := l.k.CreateProcess(procArgs) if err != nil { return 0, fmt.Errorf("failed to create process in sentry: %v", err) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 27441cbde..5fb53edb2 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -126,6 +126,8 @@ func ReadSpec(bundleDir string) (*specs.Spec, error) { // GetExecutablePath returns the absolute path to the executable, relative to // the root. It searches the environment PATH for the first file that exists // with the given name. +// TODO: Remove this in favor of finding executables via +// boot.GetExecutablePathInternal. func GetExecutablePath(exec, root string, env []string) (string, error) { exec = filepath.Clean(exec) @@ -134,18 +136,9 @@ func GetExecutablePath(exec, root string, env []string) (string, error) { return exec, nil } - // Get the PATH from the environment. - const prefix = "PATH=" - var path []string - for _, e := range env { - if strings.HasPrefix(e, prefix) { - path = strings.Split(strings.TrimPrefix(e, prefix), ":") - break - } - } - // Search the PATH for a file whose name matches the one we are looking // for. + path := GetPath(env) for _, p := range path { abs := filepath.Join(root, p, exec) // Do not follow symlink link because the target is in the container @@ -161,6 +154,18 @@ func GetExecutablePath(exec, root string, env []string) (string, error) { return exec, nil } +// GetPath returns the PATH as a slice of strings given the environemnt +// variables. +func GetPath(env []string) []string { + const prefix = "PATH=" + for _, e := range env { + if strings.HasPrefix(e, prefix) { + return strings.Split(strings.TrimPrefix(e, prefix), ":") + } + } + return nil +} + // Capabilities takes in spec and returns a TaskCapabilities corresponding to // the spec. func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) { -- cgit v1.2.3 From db81c0b02f2f947ae837a3e16471a148a66436eb Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Mon, 27 Aug 2018 11:09:06 -0700 Subject: Put fsgofer inside chroot Now each container gets its own dedicated gofer that is chroot'd to the rootfs path. This is done to add an extra layer of security in case the gofer gets compromised. PiperOrigin-RevId: 210396476 Change-Id: Iba21360a59dfe90875d61000db103f8609157ca0 --- runsc/boot/controller.go | 6 +- runsc/boot/fs.go | 6 +- runsc/boot/loader.go | 20 +++- runsc/boot/loader_test.go | 106 +++++++------------- runsc/cmd/BUILD | 1 + runsc/cmd/gofer.go | 84 ++++++++++------ runsc/cmd/state.go | 5 +- runsc/container/BUILD | 7 +- runsc/container/container.go | 96 +++++++++++++++--- runsc/container/container_test.go | 7 +- runsc/container/fs.go | 198 ++++++++++++++++++++++++++++++++++++ runsc/container/fs_test.go | 158 +++++++++++++++++++++++++++++ runsc/fsgofer/BUILD | 4 - runsc/fsgofer/control.go | 204 -------------------------------------- runsc/sandbox/BUILD | 2 - runsc/sandbox/namespace.go | 204 -------------------------------------- runsc/sandbox/network.go | 3 +- runsc/sandbox/sandbox.go | 176 ++++---------------------------- runsc/specutils/BUILD | 6 +- runsc/specutils/namespace.go | 204 ++++++++++++++++++++++++++++++++++++++ runsc/test/testutil/BUILD | 1 + runsc/test/testutil/docker.go | 6 +- runsc/test/testutil/testutil.go | 57 +++++++++++ 23 files changed, 858 insertions(+), 703 deletions(-) create mode 100644 runsc/container/fs.go create mode 100644 runsc/container/fs_test.go delete mode 100644 runsc/fsgofer/control.go delete mode 100644 runsc/sandbox/namespace.go create mode 100644 runsc/specutils/namespace.go (limited to 'runsc/specutils') diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 2d6b507b3..fdb6be5b1 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -212,11 +212,11 @@ func (cm *containerManager) Start(args *StartArgs, _ *struct{}) error { if path.Clean(args.CID) != args.CID { return fmt.Errorf("container ID shouldn't contain directory traversals such as \"..\": %q", args.CID) } - if len(args.FilePayload.Files) != 1 { - return fmt.Errorf("start arguments must contain one file for the container root") + if len(args.FilePayload.Files) == 0 { + return fmt.Errorf("start arguments must contain at least one file for the container root") } - tgid, err := cm.l.startContainer(cm.l.k, args.Spec, args.Conf, args.CID, args.FilePayload.Files[0]) + tgid, err := cm.l.startContainer(cm.l.k, args.Spec, args.Conf, args.CID, args.FilePayload.Files) if err != nil { return err } diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 6f5379a6d..20d0e42ef 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -510,8 +510,6 @@ func createRestoreEnvironment(spec *specs.Spec, conf *Config, fds *fdDispenser) MountSources: make(map[string][]fs.MountArgs), } - mounts := compileMounts(spec) - // Add root mount. fd := fds.remove() opts := p9MountOptions(conf, fd) @@ -528,8 +526,8 @@ func createRestoreEnvironment(spec *specs.Spec, conf *Config, fds *fdDispenser) } renv.MountSources[rootFsName] = append(renv.MountSources[rootFsName], rootMount) - // Add submounts - for _, m := range mounts { + // Add submounts. + for _, m := range compileMounts(spec) { if err := addRestoreMount(conf, renv, m, fds); err != nil { return nil, err } diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 0e94cf215..3963ed55d 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -23,6 +23,7 @@ import ( "runtime" "sync" "sync/atomic" + "syscall" gtime "time" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -377,7 +378,7 @@ func (l *Loader) run() error { // startContainer starts a child container. It returns the thread group ID of // the newly created process. -func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config, cid string, file *os.File) (kernel.ThreadID, error) { +func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config, cid string, files []*os.File) (kernel.ThreadID, error) { // Create capabilities. caps, err := specutils.Capabilities(spec.Process.Capabilities) if err != nil { @@ -414,11 +415,23 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config if err != nil { return 0, fmt.Errorf("failed to create new process: %v", err) } + + // Can't take ownership away from os.File. dup them to get a new FDs. + var ioFDs []int + for _, f := range files { + fd, err := syscall.Dup(int(f.Fd())) + if err != nil { + return 0, fmt.Errorf("failed to dup file: %v", err) + } + f.Close() + ioFDs = append(ioFDs, fd) + } + err = setFileSystemForProcess( &procArgs, spec, conf, - []int{int(file.Fd())}, // ioFDs + ioFDs, false, creds, procArgs.Limits, @@ -453,8 +466,7 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config return tgid, nil } -// TODO: Per-container namespaces must be supported -// for -pid. +// TODO: Per-container namespaces must be supported for -pid. // waitContainer waits for the root process of a container to exit. func (l *Loader) waitContainer(cid string, waitStatus *uint32) error { diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index f2f690b5d..2396d52c8 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -16,7 +16,6 @@ package boot import ( "fmt" - "io/ioutil" "math/rand" "os" "reflect" @@ -36,6 +35,15 @@ func init() { rand.Seed(time.Now().UnixNano()) } +func testConfig() *Config { + return &Config{ + RootDir: "unused_root_dir", + Network: NetworkNone, + FileAccess: FileAccessDirect, + DisableSeccomp: true, + } +} + // testSpec returns a simple spec that can be used in tests. func testSpec() *specs.Spec { return &specs.Spec{ @@ -55,12 +63,7 @@ func createLoader() (*Loader, error) { if err != nil { return nil, err } - conf := &Config{ - RootDir: "unused_root_dir", - Network: NetworkNone, - FileAccess: FileAccessDirect, - DisableSeccomp: true, - } + conf := testConfig() spec := testSpec() return New(spec, conf, fd, nil, false) } @@ -152,18 +155,6 @@ func TestStartSignal(t *testing.T) { // Test that MountNamespace can be created with various specs. func TestCreateMountNamespace(t *testing.T) { - conf := &Config{ - RootDir: "unused_root_dir", - FileAccess: FileAccessDirect, - DisableSeccomp: true, - } - - testFile, err := ioutil.TempFile(os.TempDir(), "create-mount-namespace-") - if err != nil { - t.Fatalf("ioutil.TempFile() failed, err: %v", err) - } - defer os.RemoveAll(testFile.Name()) - testCases := []struct { name string // Spec that will be used to create the mount manager. Note @@ -234,8 +225,7 @@ func TestCreateMountNamespace(t *testing.T) { }, { Destination: "/foo/qux", - Source: testFile.Name(), - Type: "bind", + Type: "tmpfs", }, { // File mounts with the same prefix. @@ -284,8 +274,7 @@ func TestCreateMountNamespace(t *testing.T) { { // Mount with the same prefix. Destination: "/dev/fd-foo", - Source: testFile.Name(), - Type: "bind", + Type: "tmpfs", }, { // Unsupported fs type. @@ -298,8 +287,7 @@ func TestCreateMountNamespace(t *testing.T) { }, { Destination: "/dev/bar", - Source: testFile.Name(), - Type: "bind", + Type: "tmpfs", }, }, }, @@ -339,19 +327,22 @@ func TestCreateMountNamespace(t *testing.T) { } for _, tc := range testCases { - ctx := contexttest.Context(t) - mm, err := createMountNamespace(ctx, ctx, &tc.spec, conf, nil) - if err != nil { - t.Fatalf("createMountNamespace test case %q failed: %v", tc.name, err) - } - defer mm.DecRef() - root := mm.Root() - defer root.DecRef() - for _, p := range tc.expectedPaths { - if _, err := mm.FindInode(ctx, root, root, p, 0); err != nil { - t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err) + t.Run(tc.name, func(t *testing.T) { + conf := testConfig() + ctx := contexttest.Context(t) + mm, err := createMountNamespace(ctx, ctx, &tc.spec, conf, nil) + if err != nil { + t.Fatalf("createMountNamespace test case %q failed: %v", tc.name, err) } - } + defer mm.DecRef() + root := mm.Root() + defer root.DecRef() + for _, p := range tc.expectedPaths { + if _, err := mm.FindInode(ctx, root, root, p, 0); err != nil { + t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err) + } + } + }) } } @@ -361,7 +352,7 @@ func TestRestoreEnvironment(t *testing.T) { testCases := []struct { name string spec *specs.Spec - conf *Config + fileAccess FileAccessType ioFDs []int errorExpected bool expectedRenv fs.RestoreEnvironment @@ -384,12 +375,7 @@ func TestRestoreEnvironment(t *testing.T) { }, }, }, - conf: &Config{ - RootDir: "unused_root_dir", - Network: NetworkNone, - FileAccess: FileAccessProxy, - DisableSeccomp: true, - }, + fileAccess: FileAccessProxy, ioFDs: []int{0}, errorExpected: false, expectedRenv: fs.RestoreEnvironment{ @@ -444,12 +430,7 @@ func TestRestoreEnvironment(t *testing.T) { }, }, }, - conf: &Config{ - RootDir: "unused_root_dir", - Network: NetworkNone, - FileAccess: FileAccessProxy, - DisableSeccomp: true, - }, + fileAccess: FileAccessProxy, ioFDs: []int{0, 1}, errorExpected: false, expectedRenv: fs.RestoreEnvironment{ @@ -508,12 +489,7 @@ func TestRestoreEnvironment(t *testing.T) { }, }, }, - conf: &Config{ - RootDir: "unused_root_dir", - Network: NetworkNone, - FileAccess: FileAccessProxy, - DisableSeccomp: true, - }, + fileAccess: FileAccessProxy, ioFDs: []int{0}, errorExpected: false, expectedRenv: fs.RestoreEnvironment{ @@ -572,12 +548,7 @@ func TestRestoreEnvironment(t *testing.T) { }, }, }, - conf: &Config{ - RootDir: "unused_root_dir", - Network: NetworkNone, - FileAccess: FileAccessDirect, - DisableSeccomp: true, - }, + fileAccess: FileAccessDirect, ioFDs: []int{0, 1}, errorExpected: true, }, @@ -596,20 +567,17 @@ func TestRestoreEnvironment(t *testing.T) { }, }, }, - conf: &Config{ - RootDir: "unused_root_dir", - Network: NetworkNone, - FileAccess: FileAccessDirect, - DisableSeccomp: true, - }, + fileAccess: FileAccessDirect, ioFDs: []int{0}, errorExpected: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + conf := testConfig() + conf.FileAccess = tc.fileAccess fds := &fdDispenser{fds: tc.ioFDs} - actualRenv, err := createRestoreEnvironment(tc.spec, tc.conf, fds) + actualRenv, err := createRestoreEnvironment(tc.spec, conf, fds) if !tc.errorExpected && err != nil { t.Fatalf("could not create restore environment for test:%s", tc.name) } else if tc.errorExpected { diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index b9ef4022f..5dee26a5c 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -36,6 +36,7 @@ go_library( "//pkg/p9", "//pkg/sentry/control", "//pkg/sentry/kernel/auth", + "//pkg/unet", "//pkg/urpc", "//runsc/boot", "//runsc/console", diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index e23f64d12..ab76734fc 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -16,6 +16,8 @@ package cmd import ( "os" + "path" + "sync" "syscall" "context" @@ -24,6 +26,7 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/pkg/p9" + "gvisor.googlesource.com/gvisor/pkg/unet" "gvisor.googlesource.com/gvisor/runsc/fsgofer" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -35,10 +38,6 @@ type Gofer struct { ioFDs intFlags applyCaps bool - // controllerFD is the file descriptor of a stream socket for the - // control server that is donated to this process. - controllerFD int - panicOnWrite bool } @@ -62,26 +61,16 @@ func (g *Gofer) SetFlags(f *flag.FlagSet) { f.StringVar(&g.bundleDir, "bundle", "", "path to the root of the bundle directory, defaults to the current directory") f.Var(&g.ioFDs, "io-fds", "list of FDs to connect 9P servers. They must follow this order: root first, then mounts as defined in the spec") f.BoolVar(&g.applyCaps, "apply-caps", true, "if true, apply capabilities to restrict what the Gofer process can do") - f.IntVar(&g.controllerFD, "controller-fd", -1, "required FD of a stream socket for the control server that must be donated to this process") f.BoolVar(&g.panicOnWrite, "panic-on-write", false, "if true, panics on attempts to write to RO mounts. RW mounts are unnaffected") } // Execute implements subcommands.Command. func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { - if g.bundleDir == "" || len(g.ioFDs) < 1 || g.controllerFD == -1 { + if g.bundleDir == "" || len(g.ioFDs) < 1 { f.Usage() return subcommands.ExitUsageError } - // fsgofer should run with a umask of 0, because we want to preserve file - // modes exactly as sent by the sandbox, which will have applied its own umask. - syscall.Umask(0) - - spec, err := specutils.ReadSpec(g.bundleDir) - if err != nil { - Fatalf("error reading spec: %v", err) - } - if g.applyCaps { // Minimal set of capabilities needed by the Gofer to operate on files. caps := []string{ @@ -107,49 +96,84 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) panic("unreachable") } + spec, err := specutils.ReadSpec(g.bundleDir) + if err != nil { + Fatalf("error reading spec: %v", err) + } specutils.LogSpec(spec) - // Start with root mount, then add any other addition mount as needed. + // fsgofer should run with a umask of 0, because we want to preserve file + // modes exactly as sent by the sandbox, which will have applied its own umask. + syscall.Umask(0) + + // Find what path is going to be served by this gofer. + root := absPath(g.bundleDir, spec.Root.Path) + if err := syscall.Chroot(root); err != nil { + Fatalf("failed to chroot to %q: %v", root, err) + } + if err := syscall.Chdir("/"); err != nil { + Fatalf("failed to change working dir: %v", err) + } + log.Infof("Process chroot'd to %q", root) + + // Start with root mount, then add any other additional mount as needed. ats := make([]p9.Attacher, 0, len(spec.Mounts)+1) - p := absPath(g.bundleDir, spec.Root.Path) - ats = append(ats, fsgofer.NewAttachPoint(p, fsgofer.Config{ + ats = append(ats, fsgofer.NewAttachPoint("/", fsgofer.Config{ ROMount: spec.Root.Readonly, PanicOnWrite: g.panicOnWrite, // Docker uses overlay2 by default for the root mount, and overlay2 does a copy-up when // each file is opened as writable. Thus, we open files lazily to avoid copy-up. LazyOpenForWrite: true, })) - log.Infof("Serving %q mapped to %q on FD %d", "/", p, g.ioFDs[0]) + log.Infof("Serving %q mapped to %q on FD %d (ro: %t)", "/", root, g.ioFDs[0], spec.Root.Readonly) mountIdx := 1 // first one is the root for _, m := range spec.Mounts { if specutils.Is9PMount(m) { - p = absPath(g.bundleDir, m.Source) - ats = append(ats, fsgofer.NewAttachPoint(p, fsgofer.Config{ + if !path.IsAbs(m.Destination) { + Fatalf("destination must be absolute path: %v", m.Destination) + } + cfg := fsgofer.Config{ ROMount: isReadonlyMount(m.Options), PanicOnWrite: g.panicOnWrite, LazyOpenForWrite: false, - })) + } + ats = append(ats, fsgofer.NewAttachPoint(m.Destination, cfg)) if mountIdx >= len(g.ioFDs) { Fatalf("No FD found for mount. Did you forget --io-fd? mount: %d, %v", len(g.ioFDs), m) } - log.Infof("Serving %q mapped to %q on FD %d", m.Destination, p, g.ioFDs[mountIdx]) + log.Infof("Serving %q mapped on FD %d (ro: %t)", m.Destination, g.ioFDs[mountIdx], cfg.ROMount) mountIdx++ } } if mountIdx != len(g.ioFDs) { - Fatalf("Too many FDs passed for mounts. mounts: %d, FDs: %d", mountIdx, len(g.ioFDs)) + Fatalf("too many FDs passed for mounts. mounts: %d, FDs: %d", mountIdx, len(g.ioFDs)) } - ctrl, err := fsgofer.NewController(g.controllerFD, g.bundleDir) + runServers(ats, g.ioFDs) + return subcommands.ExitSuccess +} - if err := ctrl.Serve(ats, g.ioFDs); err != nil { - Fatalf("Failed to serve via P9: %v", err) +func runServers(ats []p9.Attacher, ioFDs []int) { + // Run the loops and wait for all to exit. + var wg sync.WaitGroup + for i, ioFD := range ioFDs { + wg.Add(1) + go func(ioFD int, at p9.Attacher) { + socket, err := unet.NewSocket(ioFD) + if err != nil { + Fatalf("err creating server on FD %d: %v", ioFD, err) + } + s := p9.NewServer(at) + if err := s.Handle(socket); err != nil { + Fatalf("P9 server returned error. Gofer is shutting down. FD: %d, err: %v", ioFD, err) + } + wg.Done() + }(ioFD, ats[i]) } - ctrl.Wait() - - return subcommands.ExitSuccess + wg.Wait() + log.Infof("All 9P servers exited.") } func isReadonlyMount(opts []string) bool { diff --git a/runsc/cmd/state.go b/runsc/cmd/state.go index 28752d95e..265014e1b 100644 --- a/runsc/cmd/state.go +++ b/runsc/cmd/state.go @@ -63,8 +63,11 @@ func (*State) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s } log.Debugf("Returning state for container %+v", c) + state := c.State() + log.Debugf("State: %+v", state) + // Write json-encoded state directly to stdout. - b, err := json.MarshalIndent(c.State(), "", " ") + b, err := json.MarshalIndent(state, "", " ") if err != nil { Fatalf("error marshaling container state: %v", err) } diff --git a/runsc/container/BUILD b/runsc/container/BUILD index e40ca4709..cba418d0c 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -13,6 +13,7 @@ go_library( name = "container", srcs = [ "container.go", + "fs.go", "hook.go", "status.go", ], @@ -28,13 +29,17 @@ go_library( "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@org_golang_x_sys//unix:go_default_library", ], ) go_test( name = "container_test", size = "medium", - srcs = ["container_test.go"], + srcs = [ + "container_test.go", + "fs_test.go", + ], data = [ ":uds_test_app", "//runsc", diff --git a/runsc/container/container.go b/runsc/container/container.go index 8bd47aac1..16af66d3e 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" "regexp" "strconv" @@ -223,15 +224,19 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // init container in the sandbox. if specutils.ShouldCreateSandbox(spec) || !conf.MultiContainer { log.Debugf("Creating new sandbox for container %q", id) + ioFiles, err := c.createGoferProcess(spec, conf, bundleDir) + if err != nil { + return nil, err + } + // Start a new sandbox for this container. Any errors after this point // must destroy the container. - s, goferPid, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket) + s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket, ioFiles) if err != nil { c.Destroy() return nil, err } c.Sandbox = s - c.GoferPid = goferPid } else { // This is sort of confusing. For a sandbox with a root // container and a child container in it, runsc sees: @@ -254,13 +259,6 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo return nil, err } c.Sandbox = sb.Sandbox - - // Prepare the gofer to serve the container's filesystem. - err = sb.Sandbox.CreateChild(c.ID, bundleDir) - if err != nil { - c.Destroy() - return nil, err - } } c.Status = Created @@ -304,7 +302,12 @@ func (c *Container) Start(conf *boot.Config) error { return err } } else { - if err := c.Sandbox.Start(c.Spec, conf, c.ID); err != nil { + // Create the gofer process. + ioFiles, err := c.createGoferProcess(c.Spec, conf, c.BundleDir) + if err != nil { + return err + } + if err := c.Sandbox.Start(c.Spec, conf, c.ID, ioFiles); err != nil { c.Destroy() return err } @@ -518,6 +521,8 @@ func (c *Container) Destroy() error { log.Warningf("Failed to destroy sandbox %q: %v", c.Sandbox.ID, err) } } + c.Sandbox = nil + if c.GoferPid != 0 { log.Debugf("Killing gofer for container %q, PID: %d", c.ID, c.GoferPid) if err := syscall.Kill(c.GoferPid, syscall.SIGKILL); err != nil { @@ -527,9 +532,7 @@ func (c *Container) Destroy() error { } } - c.Sandbox = nil c.Status = Stopped - return nil } @@ -596,3 +599,72 @@ func (c *Container) waitForStopped() error { } return backoff.Retry(op, b) } + +func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]*os.File, error) { + if conf.FileAccess == boot.FileAccessDirect { + // Don't start a gofer. The sandbox will access host FS directly. + return nil, nil + } + + if err := setupFS(spec, conf, bundleDir); err != nil { + return nil, fmt.Errorf("failed to setup mounts: %v", err) + } + + // Start with the general config flags. + args := conf.ToFlags() + args = append(args, "gofer", "--bundle", bundleDir) + if conf.Overlay { + args = append(args, "--panic-on-write=true") + } + + // Add root mount and then add any other additional mounts. + mountCount := 1 + + // Add additional mounts. + for _, m := range spec.Mounts { + if specutils.Is9PMount(m) { + mountCount++ + } + } + sandEnds := make([]*os.File, 0, mountCount) + goferEnds := make([]*os.File, 0, mountCount) + + // nextFD is the next available file descriptor for the gofer process. + // It starts at 3 because 0-2 are used by stdin/stdout/stderr. + nextFD := 3 + for ; nextFD-3 < mountCount; nextFD++ { + fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) + if err != nil { + return nil, err + } + sandEnds = append(sandEnds, os.NewFile(uintptr(fds[0]), "sandbox io fd")) + + goferEnd := os.NewFile(uintptr(fds[1]), "gofer io fd") + defer goferEnd.Close() + goferEnds = append(goferEnds, goferEnd) + + args = append(args, fmt.Sprintf("--io-fds=%d", nextFD)) + } + + binPath, err := specutils.BinPath() + if err != nil { + return nil, err + } + cmd := exec.Command(binPath, args...) + cmd.ExtraFiles = goferEnds + + // Setup any uid/gid mappings, and create or join the configured user + // namespace so the gofer's view of the filesystem aligns with the + // users in the sandbox. + specutils.SetUIDGIDMappings(cmd, spec) + nss := specutils.FilterNS([]specs.LinuxNamespaceType{specs.UserNamespace}, spec) + + // Start the gofer in the given namespace. + log.Debugf("Starting gofer: %s %v", binPath, args) + if err := specutils.StartInNS(cmd, nss); err != nil { + return nil, err + } + log.Infof("Gofer started, pid: %d", cmd.Process.Pid) + c.GoferPid = cmd.Process.Pid + return sandEnds, nil +} diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 6d84700ce..25aaf3f86 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -1211,9 +1211,6 @@ func TestMountNewDir(t *testing.T) { if err != nil { t.Fatal("ioutil.TempDir() failed:", err) } - if err := os.Chmod(root, 0755); err != nil { - t.Fatalf("os.Chmod(%q) failed: %v", root, err) - } srcDir := path.Join(root, "src", "dir", "anotherdir") if err := os.MkdirAll(srcDir, 0755); err != nil { @@ -1747,3 +1744,7 @@ func TestGoferExits(t *testing.T) { t.Errorf("container shouldn't be running, container: %+v", c) } } + +func TestMain(m *testing.M) { + testutil.RunAsRoot(m) +} diff --git a/runsc/container/fs.go b/runsc/container/fs.go new file mode 100644 index 000000000..652f81bbf --- /dev/null +++ b/runsc/container/fs.go @@ -0,0 +1,198 @@ +// Copyright 2018 Google Inc. +// +// 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 container + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "syscall" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/specutils" +) + +type mapping struct { + set bool + val uint32 +} + +var optionsMap = map[string]mapping{ + "acl": {set: true, val: syscall.MS_POSIXACL}, + "async": {set: false, val: syscall.MS_SYNCHRONOUS}, + "atime": {set: false, val: syscall.MS_NOATIME}, + "bind": {set: true, val: syscall.MS_BIND}, + "defaults": {set: true, val: 0}, + "dev": {set: false, val: syscall.MS_NODEV}, + "diratime": {set: false, val: syscall.MS_NODIRATIME}, + "dirsync": {set: true, val: syscall.MS_DIRSYNC}, + "exec": {set: false, val: syscall.MS_NOEXEC}, + "iversion": {set: true, val: syscall.MS_I_VERSION}, + "loud": {set: false, val: syscall.MS_SILENT}, + "mand": {set: true, val: syscall.MS_MANDLOCK}, + "noacl": {set: false, val: syscall.MS_POSIXACL}, + "noatime": {set: true, val: syscall.MS_NOATIME}, + "nodev": {set: true, val: syscall.MS_NODEV}, + "nodiratime": {set: true, val: syscall.MS_NODIRATIME}, + "noexec": {set: true, val: syscall.MS_NOEXEC}, + "noiversion": {set: false, val: syscall.MS_I_VERSION}, + "nomand": {set: false, val: syscall.MS_MANDLOCK}, + "norelatime": {set: false, val: syscall.MS_RELATIME}, + "nostrictatime": {set: false, val: syscall.MS_STRICTATIME}, + "nosuid": {set: true, val: syscall.MS_NOSUID}, + "private": {set: true, val: syscall.MS_PRIVATE}, + "rbind": {set: true, val: syscall.MS_BIND | syscall.MS_REC}, + "relatime": {set: true, val: syscall.MS_RELATIME}, + "remount": {set: true, val: syscall.MS_REMOUNT}, + "ro": {set: true, val: syscall.MS_RDONLY}, + "rprivate": {set: true, val: syscall.MS_PRIVATE | syscall.MS_REC}, + "rw": {set: false, val: syscall.MS_RDONLY}, + "silent": {set: true, val: syscall.MS_SILENT}, + "strictatime": {set: true, val: syscall.MS_STRICTATIME}, + "suid": {set: false, val: syscall.MS_NOSUID}, + "sync": {set: true, val: syscall.MS_SYNCHRONOUS}, +} + +// setupFS creates the container directory structure under 'spec.Root.Path'. +// This allows the gofer serving the containers to be chroot under this +// directory to create an extra layer to security in case the gofer gets +// compromised. +func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { + for _, m := range spec.Mounts { + if m.Type != "bind" || !specutils.IsSupportedDevMount(m) { + continue + } + src := m.Source + if !filepath.IsAbs(src) { + src = filepath.Join(bundleDir, src) + } + srcfi, err := os.Stat(src) + if err != nil { + return err + } + + // It's possible that 'm.Destination' follows symlinks inside the + // container. + dst, err := resolveSymlinks(spec.Root.Path, m.Destination) + if err != nil { + return err + } + + // Create mount point if it doesn't exits + if _, err := os.Stat(dst); os.IsNotExist(err) { + if srcfi.IsDir() { + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + } else { + if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { + return err + } + f, err := os.OpenFile(dst, os.O_CREATE, 0755) + if err != nil { + return err + } + f.Close() + } + } + + flags := optionsToFlags(m.Options) + flags |= syscall.MS_BIND + log.Infof("Mounting src: %q, dst: %q, flags: %#x", src, dst, flags) + if err := syscall.Mount(src, dst, m.Type, uintptr(flags), ""); err != nil { + return err + } + } + + // Remount root as readonly after setup is done, if requested. + if spec.Root.Readonly { + log.Infof("Remounting root as readonly: %q", spec.Root.Path) + flags := uintptr(syscall.MS_BIND | syscall.MS_REMOUNT | syscall.MS_RDONLY | syscall.MS_REC) + return unix.Mount(spec.Root.Path, spec.Root.Path, "bind", flags, "") + } + return nil +} + +// resolveSymlinks walks 'rel' having 'root' as the root directory. If there are +// symlinks, they are evaluated relative to 'root' to ensure the end result is +// the same as if the process was running inside the container. +func resolveSymlinks(root, rel string) (string, error) { + return resolveSymlinksImpl(root, root, rel, 255) +} + +func resolveSymlinksImpl(root, base, rel string, followCount uint) (string, error) { + if followCount == 0 { + return "", fmt.Errorf("too many symlinks to follow, path: %q", filepath.Join(base, rel)) + } + + rel = filepath.Clean(rel) + for _, name := range strings.Split(rel, string(filepath.Separator)) { + if name == "" { + continue + } + // Note that Join() resolves things like ".." and returns a clean path. + path := filepath.Join(base, name) + if !strings.HasPrefix(path, root) { + // One cannot '..' their way out of root. + path = root + continue + } + fi, err := os.Lstat(path) + if err != nil { + if !os.IsNotExist(err) { + return "", err + } + // Not found means there is no symlink to check. Just keep walking dirs. + base = path + continue + } + if fi.Mode()&os.ModeSymlink != 0 { + link, err := os.Readlink(path) + if err != nil { + return "", err + } + if filepath.IsAbs(link) { + base = root + } + base, err = resolveSymlinksImpl(root, base, link, followCount-1) + if err != nil { + return "", err + } + continue + } + base = path + } + return base, nil +} + +func optionsToFlags(opts []string) uint32 { + var rv uint32 + for _, opt := range opts { + if m, ok := optionsMap[opt]; ok { + if m.set { + rv |= m.val + } else { + rv ^= m.val + } + } else { + log.Warningf("Ignoring mount option %q", opt) + } + } + return rv +} diff --git a/runsc/container/fs_test.go b/runsc/container/fs_test.go new file mode 100644 index 000000000..84bde18fb --- /dev/null +++ b/runsc/container/fs_test.go @@ -0,0 +1,158 @@ +// Copyright 2018 Google Inc. +// +// 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 container + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" + + "gvisor.googlesource.com/gvisor/runsc/test/testutil" +) + +type dir struct { + rel string + link string +} + +func construct(root string, dirs []dir) error { + for _, d := range dirs { + p := path.Join(root, d.rel) + if d.link == "" { + if err := os.MkdirAll(p, 0755); err != nil { + return fmt.Errorf("error creating dir: %v", err) + } + } else { + if err := os.MkdirAll(path.Dir(p), 0755); err != nil { + return fmt.Errorf("error creating dir: %v", err) + } + if err := os.Symlink(d.link, p); err != nil { + return fmt.Errorf("error creating symlink: %v", err) + } + } + } + return nil +} + +func TestResolveSymlinks(t *testing.T) { + root, err := ioutil.TempDir(testutil.TmpDir(), "root") + if err != nil { + t.Fatal("ioutil.TempDir() failed:", err) + } + dirs := []dir{ + {"dir1/dir11/dir111/dir1111", ""}, // Just a boring dir + {"dir1/lnk12", "dir11"}, // Link to sibling + {"dir1/lnk13", "./dir11"}, // Link to sibling through self + {"dir1/lnk14", "../dir1/dir11"}, // Link to sibling through parent + {"dir1/dir15/lnk151", ".."}, // Link to parent + {"dir1/lnk16", "dir11/dir111"}, // Link to child + {"dir1/lnk17", "."}, // Link to self + {"dir1/lnk18", "lnk13"}, // Link to link + {"lnk2", "dir1/lnk13"}, // Link to link to link + {"dir3/dir21/lnk211", "../.."}, // Link to root relative + {"dir3/lnk22", "/"}, // Link to root absolute + {"dir3/lnk23", "/dir1"}, // Link to dir absolute + {"dir3/lnk24", "/dir1/lnk12"}, // Link to link absolute + {"lnk5", "../../.."}, // Link outside root + } + if err := construct(root, dirs); err != nil { + t.Fatal("construct failed:", err) + } + + tests := []struct { + name string + rel string + want string + compareHost bool + }{ + {name: "root", rel: "/", want: "/", compareHost: true}, + {name: "basic dir", rel: "/dir1/dir11/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "dot 1", rel: "/dir1/dir11/./dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "dot 2", rel: "/dir1/././dir11/./././././dir111/.", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "dotdot 1", rel: "/dir1/dir11/../dir15", want: "/dir1/dir15", compareHost: true}, + {name: "dotdot 2", rel: "/dir1/dir11/dir1111/../..", want: "/dir1", compareHost: true}, + + {name: "link sibling", rel: "/dir1/lnk12", want: "/dir1/dir11", compareHost: true}, + {name: "link sibling + dir", rel: "/dir1/lnk12/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "link sibling through self", rel: "/dir1/lnk13", want: "/dir1/dir11", compareHost: true}, + {name: "link sibling through parent", rel: "/dir1/lnk14", want: "/dir1/dir11", compareHost: true}, + + {name: "link parent", rel: "/dir1/dir15/lnk151", want: "/dir1", compareHost: true}, + {name: "link parent + dir", rel: "/dir1/dir15/lnk151/dir11", want: "/dir1/dir11", compareHost: true}, + {name: "link child", rel: "/dir1/lnk16", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "link child + dir", rel: "/dir1/lnk16/dir1111", want: "/dir1/dir11/dir111/dir1111", compareHost: true}, + {name: "link self", rel: "/dir1/lnk17", want: "/dir1", compareHost: true}, + {name: "link self + dir", rel: "/dir1/lnk17/dir11", want: "/dir1/dir11", compareHost: true}, + + {name: "link^2", rel: "/dir1/lnk18", want: "/dir1/dir11", compareHost: true}, + {name: "link^2 + dir", rel: "/dir1/lnk18/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "link^3", rel: "/lnk2", want: "/dir1/dir11", compareHost: true}, + {name: "link^3 + dir", rel: "/lnk2/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + + {name: "link abs", rel: "/dir3/lnk23", want: "/dir1"}, + {name: "link abs + dir", rel: "/dir3/lnk23/dir11", want: "/dir1/dir11"}, + {name: "link^2 abs", rel: "/dir3/lnk24", want: "/dir1/dir11"}, + {name: "link^2 abs + dir", rel: "/dir3/lnk24/dir111", want: "/dir1/dir11/dir111"}, + + {name: "root link rel", rel: "/dir3/dir21/lnk211", want: "/", compareHost: true}, + {name: "root link abs", rel: "/dir3/lnk22", want: "/"}, + {name: "root contain link", rel: "/lnk5/dir1", want: "/dir1"}, + {name: "root contain dotdot", rel: "/dir1/dir11/../../../../../../../..", want: "/"}, + + {name: "crazy", rel: "/dir3/dir21/lnk211/dir3/lnk22/dir1/dir11/../../lnk5/dir3/../dir3/lnk24/dir111/dir1111/..", want: "/dir1/dir11/dir111"}, + } + for _, tst := range tests { + t.Run(tst.name, func(t *testing.T) { + got, err := resolveSymlinks(root, tst.rel) + if err != nil { + t.Errorf("resolveSymlinks(root, %q) failed: %v", tst.rel, err) + } + want := path.Join(root, tst.want) + if got != want { + t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, got, want) + } + if tst.compareHost { + // Check that host got to the same end result. + host, err := filepath.EvalSymlinks(path.Join(root, tst.rel)) + if err != nil { + t.Errorf("path.EvalSymlinks(root, %q) failed: %v", tst.rel, err) + } + if host != got { + t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, host, got) + } + } + }) + } +} + +func TestResolveSymlinksLoop(t *testing.T) { + root, err := ioutil.TempDir(testutil.TmpDir(), "root") + if err != nil { + t.Fatal("ioutil.TempDir() failed:", err) + } + dirs := []dir{ + {"loop1", "loop2"}, + {"loop2", "loop1"}, + } + if err := construct(root, dirs); err != nil { + t.Fatal("construct failed:", err) + } + if _, err := resolveSymlinks(root, "loop1"); err == nil { + t.Errorf("resolveSymlinks() should have failed") + } +} diff --git a/runsc/fsgofer/BUILD b/runsc/fsgofer/BUILD index 0bc682b5f..24e172f48 100644 --- a/runsc/fsgofer/BUILD +++ b/runsc/fsgofer/BUILD @@ -5,7 +5,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "fsgofer", srcs = [ - "control.go", "fsgofer.go", "fsgofer_unsafe.go", ], @@ -15,12 +14,9 @@ go_library( ], deps = [ "//pkg/abi/linux", - "//pkg/control/server", "//pkg/fd", "//pkg/log", "//pkg/p9", - "//pkg/unet", - "//pkg/urpc", "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/runsc/fsgofer/control.go b/runsc/fsgofer/control.go deleted file mode 100644 index 8cb2f67ac..000000000 --- a/runsc/fsgofer/control.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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 fsgofer - -import ( - "fmt" - "os" - "path/filepath" - "sync" - - "gvisor.googlesource.com/gvisor/pkg/control/server" - "gvisor.googlesource.com/gvisor/pkg/log" - "gvisor.googlesource.com/gvisor/pkg/p9" - "gvisor.googlesource.com/gvisor/pkg/unet" - "gvisor.googlesource.com/gvisor/pkg/urpc" -) - -// Controller manages the fsgofer's control server. -type Controller struct { - // api holds the control server's URPC endpoints. - api api - - // srv is the control server. - srv *server.Server -} - -// NewController creates a new Controller and starts it listenting -func NewController(fd int, rootBundleDir string) (*Controller, error) { - if !filepath.IsAbs(rootBundleDir) { - return nil, fmt.Errorf("NewController should receive an absolute bundle dir path, but got %q", rootBundleDir) - } - - srv, err := server.CreateFromFD(fd) - if err != nil { - return nil, err - } - - cr := &Controller{srv: srv} - cr.api.rootBundleDir = rootBundleDir - cr.api.bundleDirs = make(map[string]string) - srv.Register(&cr.api) - - if err := srv.StartServing(); err != nil { - return nil, err - } - - return cr, nil -} - -// Wait waits for all the p9 servers to finish, then shuts down the control -// server. -func (cr *Controller) Wait() { - cr.api.p9wg.Wait() - cr.srv.Stop() - log.Infof("All 9P servers exited.") -} - -// Serve starts serving each Attacher in ats via its corresponding file -// descriptor in ioFDs. This takes ownership of the FDs in ioFDs. -func (cr *Controller) Serve(ats []p9.Attacher, ioFDs []int) error { - if len(ats) != len(ioFDs) { - return fmt.Errorf("number of attach points does not match the number of IO FDs (%d and %d)", len(ats), len(ioFDs)) - } - for i, _ := range ats { - cr.api.serve(ats[i], os.NewFile(uintptr(ioFDs[i]), "io fd")) - } - return nil -} - -// api URPC methods. -const ( - // AddBundleDirs readies the gofer to serve from a new bundle - // directory. It should be called during runsc create. - AddBundleDirs = "api.AddBundleDirs" - - // ServeDirectory serves a new directory via the fsgofer. It should be - // called during runsc start. - ServeDirectory = "api.ServeDirectory" -) - -// API defines and implements the URPC endpoints for the gofer. -type api struct { - // p9wg waits for all the goroutines serving the sentry via p9. When its - // counter is 0, the gofer is out of work and exits. - p9wg sync.WaitGroup - - // bundleDirs maps from container ID to bundle directory for each - // container. - bundleDirs map[string]string - - // rootBundleDir is the bundle directory of the root container. - rootBundleDir string -} - -// AddBundleDirsRequest is the URPC argument to AddBundleDirs. -type AddBundleDirsRequest struct { - // BundleDirs is a map of container IDs to bundle directories to add to - // the gofer. - BundleDirs map[string]string -} - -// AddBundleDirsRequest adds bundle directories that for the gofer to serve. -func (api *api) AddBundleDirs(req *AddBundleDirsRequest, _ *struct{}) error { - log.Debugf("fsgofer.AddBundleDirs") - for cid, bd := range req.BundleDirs { - if _, ok := api.bundleDirs[cid]; ok { - return fmt.Errorf("fsgofer already has a bundleDir for container %q", cid) - } - api.bundleDirs[cid] = bd - } - return nil -} - -// ServeDirectoryRequest is the URPC argument to ServeDirectory. -type ServeDirectoryRequest struct { - // Dir is the absolute path to a directory to be served to the sentry. - Dir string - - // IsReadOnly specifies whether the directory should be served in - // read-only mode. - IsReadOnly bool - - // CID is the container ID of the container that needs to serve a - // directory. - CID string - - // FilePayload contains the socket over which the sentry will request - // files from Dir. - urpc.FilePayload -} - -// ServeDirectory begins serving a directory via a file descriptor for the -// sentry. Directories must be added via AddBundleDirsRequest before -// ServeDirectory is called. -func (api *api) ServeDirectory(req *ServeDirectoryRequest, _ *struct{}) error { - log.Debugf("fsgofer.ServeDirectory: %+v", req) - - if req.Dir == "" { - return fmt.Errorf("ServeDirectory should receive a directory argument, but was empty") - } - if req.CID == "" { - return fmt.Errorf("ServeDirectory should receive a CID argument, but was empty") - } - // Prevent CIDs containing ".." from confusing the sentry when creating - // /containers/ directory. - // TODO: Once we have multiple independant roots, this - // check won't be necessary. - if filepath.Clean(req.CID) != req.CID { - return fmt.Errorf("container ID shouldn't contain directory traversals such as \"..\": %q", req.CID) - } - if nFiles := len(req.FilePayload.Files); nFiles != 1 { - return fmt.Errorf("ServeDirectory should receive 1 file descriptor, but got %d", nFiles) - } - - bd, ok := api.bundleDirs[req.CID] - if !ok { - // If there's no entry in bundleDirs for the container ID, this - // is the root container. - bd = api.rootBundleDir - } - - // Relative paths are served relative to the bundle directory. - absDir := req.Dir - if !filepath.IsAbs(absDir) { - absDir = filepath.Join(bd, req.Dir) - } - - // Create the attach point and start serving. - at := NewAttachPoint(absDir, Config{ - ROMount: req.IsReadOnly, - LazyOpenForWrite: true, - }) - api.serve(at, req.FilePayload.Files[0]) - - return nil -} - -// serve begins serving a directory via a file descriptor. -func (api *api) serve(at p9.Attacher, ioFile *os.File) { - api.p9wg.Add(1) - go func() { - socket, err := unet.NewSocket(int(ioFile.Fd())) - if err != nil { - panic(fmt.Sprintf("err creating server on FD %d: %v", ioFile.Fd(), err)) - } - s := p9.NewServer(at) - if err := s.Handle(socket); err != nil { - panic(fmt.Sprintf("P9 server returned error. Gofer is shutting down. FD: %d, err: %v", ioFile.Fd(), err)) - } - api.p9wg.Done() - }() -} diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index e9a39f797..9317b1c14 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -5,7 +5,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "sandbox", srcs = [ - "namespace.go", "network.go", "sandbox.go", ], @@ -21,7 +20,6 @@ go_library( "//pkg/urpc", "//runsc/boot", "//runsc/console", - "//runsc/fsgofer", "//runsc/specutils", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", "@com_github_vishvananda_netlink//:go_default_library", diff --git a/runsc/sandbox/namespace.go b/runsc/sandbox/namespace.go deleted file mode 100644 index 1d3bcfbb5..000000000 --- a/runsc/sandbox/namespace.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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 sandbox - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" - "syscall" - - specs "github.com/opencontainers/runtime-spec/specs-go" - "golang.org/x/sys/unix" - "gvisor.googlesource.com/gvisor/pkg/log" -) - -// nsCloneFlag returns the clone flag that can be used to set a namespace of -// the given type. -func nsCloneFlag(nst specs.LinuxNamespaceType) uintptr { - switch nst { - case specs.IPCNamespace: - return syscall.CLONE_NEWIPC - case specs.MountNamespace: - return syscall.CLONE_NEWNS - case specs.NetworkNamespace: - return syscall.CLONE_NEWNET - case specs.PIDNamespace: - return syscall.CLONE_NEWPID - case specs.UTSNamespace: - return syscall.CLONE_NEWUTS - case specs.UserNamespace: - return syscall.CLONE_NEWUSER - case specs.CgroupNamespace: - panic("cgroup namespace has no associated clone flag") - default: - panic(fmt.Sprintf("unknown namespace %v", nst)) - } -} - -// nsPath returns the path of the namespace for the current process and the -// given namespace. -func nsPath(nst specs.LinuxNamespaceType) string { - base := "/proc/self/ns" - switch nst { - case specs.CgroupNamespace: - return filepath.Join(base, "cgroup") - case specs.IPCNamespace: - return filepath.Join(base, "ipc") - case specs.MountNamespace: - return filepath.Join(base, "mnt") - case specs.NetworkNamespace: - return filepath.Join(base, "net") - case specs.PIDNamespace: - return filepath.Join(base, "pid") - case specs.UserNamespace: - return filepath.Join(base, "user") - case specs.UTSNamespace: - return filepath.Join(base, "uts") - default: - panic(fmt.Sprintf("unknown namespace %v", nst)) - } -} - -// getNS returns true and the namespace with the given type from the slice of -// namespaces in the spec. It returns false if the slice does not contain a -// namespace with the type. -func getNS(nst specs.LinuxNamespaceType, s *specs.Spec) (specs.LinuxNamespace, bool) { - if s.Linux == nil { - return specs.LinuxNamespace{}, false - } - for _, ns := range s.Linux.Namespaces { - if ns.Type == nst { - return ns, true - } - } - return specs.LinuxNamespace{}, false -} - -// filterNS returns a slice of namespaces from the spec with types that match -// those in the `filter` slice. -func filterNS(filter []specs.LinuxNamespaceType, s *specs.Spec) []specs.LinuxNamespace { - if s.Linux == nil { - return nil - } - var out []specs.LinuxNamespace - for _, nst := range filter { - if ns, ok := getNS(nst, s); ok { - out = append(out, ns) - } - } - return out -} - -// setNS sets the namespace of the given type. It must be called with -// OSThreadLocked. -func setNS(fd, nsType uintptr) error { - if _, _, err := syscall.RawSyscall(unix.SYS_SETNS, fd, nsType, 0); err != 0 { - return err - } - return nil -} - -// applyNS applies the namespace on the current thread and returns a function -// that will restore the namespace to the original value. -// -// Preconditions: Must be called with os thread locked. -func applyNS(ns specs.LinuxNamespace) (func(), error) { - log.Infof("applying namespace %v at path %q", ns.Type, ns.Path) - newNS, err := os.Open(ns.Path) - if err != nil { - return nil, fmt.Errorf("error opening %q: %v", ns.Path, err) - } - defer newNS.Close() - - // Store current netns to restore back after child is started. - curPath := nsPath(ns.Type) - oldNS, err := os.Open(curPath) - if err != nil { - return nil, fmt.Errorf("error opening %q: %v", curPath, err) - } - - // Set netns to the one requested and setup function to restore it back. - flag := nsCloneFlag(ns.Type) - if err := setNS(newNS.Fd(), flag); err != nil { - oldNS.Close() - return nil, fmt.Errorf("error setting namespace of type %v and path %q: %v", ns.Type, ns.Path, err) - } - return func() { - log.Infof("restoring namespace %v", ns.Type) - defer oldNS.Close() - if err := setNS(oldNS.Fd(), flag); err != nil { - panic(fmt.Sprintf("error restoring namespace: of type %v: %v", ns.Type, err)) - } - }, nil -} - -// startInNS joins or creates the given namespaces and calls cmd.Start before -// restoring the namespaces to the original values. -func startInNS(cmd *exec.Cmd, nss []specs.LinuxNamespace) error { - // We are about to setup namespaces, which requires the os thread being - // locked so that Go doesn't change the thread out from under us. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if cmd.SysProcAttr == nil { - cmd.SysProcAttr = &syscall.SysProcAttr{} - } - - for _, ns := range nss { - if ns.Path == "" { - // No path. Just set a flag to create a new namespace. - cmd.SysProcAttr.Cloneflags |= nsCloneFlag(ns.Type) - continue - } - // Join the given namespace, and restore the current namespace - // before exiting. - restoreNS, err := applyNS(ns) - if err != nil { - return err - } - defer restoreNS() - } - - return cmd.Start() -} - -// setUIDGIDMappings sets the given uid/gid mappings from the spec on the cmd. -func setUIDGIDMappings(cmd *exec.Cmd, s *specs.Spec) { - if s.Linux == nil { - return - } - if cmd.SysProcAttr == nil { - cmd.SysProcAttr = &syscall.SysProcAttr{} - } - for _, idMap := range s.Linux.UIDMappings { - log.Infof("Mapping host uid %d to container uid %d (size=%d)", idMap.HostID, idMap.ContainerID, idMap.Size) - cmd.SysProcAttr.UidMappings = append(cmd.SysProcAttr.UidMappings, syscall.SysProcIDMap{ - ContainerID: int(idMap.ContainerID), - HostID: int(idMap.HostID), - Size: int(idMap.Size), - }) - } - for _, idMap := range s.Linux.GIDMappings { - log.Infof("Mapping host gid %d to container gid %d (size=%d)", idMap.HostID, idMap.ContainerID, idMap.Size) - cmd.SysProcAttr.GidMappings = append(cmd.SysProcAttr.GidMappings, syscall.SysProcIDMap{ - ContainerID: int(idMap.ContainerID), - HostID: int(idMap.HostID), - Size: int(idMap.Size), - }) - } -} diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index d0ce6228b..8694ba755 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -29,6 +29,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/pkg/urpc" "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/specutils" ) const ( @@ -132,7 +133,7 @@ func createDefaultLoopbackInterface(conn *urpc.Client) error { func joinNetNS(nsPath string) (func(), error) { runtime.LockOSThread() - restoreNS, err := applyNS(specs.LinuxNamespace{ + restoreNS, err := specutils.ApplyNS(specs.LinuxNamespace{ Type: specs.NetworkNamespace, Path: nsPath, }) diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index e54ba4ba3..f14a2f8c9 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -32,7 +32,6 @@ import ( "gvisor.googlesource.com/gvisor/pkg/urpc" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/console" - "gvisor.googlesource.com/gvisor/runsc/fsgofer" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -55,31 +54,20 @@ type Sandbox struct { } // Create creates the sandbox process. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string) (*Sandbox, int, error) { +func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string, ioFiles []*os.File) (*Sandbox, error) { s := &Sandbox{ID: id} - binPath, err := specutils.BinPath() - if err != nil { - return nil, 0, err - } - - // Create the gofer process. - goferPid, ioFiles, err := s.createGoferProcess(spec, conf, bundleDir, binPath) - if err != nil { - return nil, 0, err - } - // Create the sandbox process. - if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, binPath, ioFiles); err != nil { - return nil, 0, err + if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, ioFiles); err != nil { + return nil, err } // Wait for the control server to come up (or timeout). if err := s.waitForCreated(10 * time.Second); err != nil { - return nil, 0, err + return nil, err } - return s, goferPid, nil + return s, nil } // StartRoot starts running the root container process inside the sandbox. @@ -105,70 +93,29 @@ func (s *Sandbox) StartRoot(spec *specs.Spec, conf *boot.Config) error { return nil } -// CreateChild creates a non-root container inside the sandbox. -func (s *Sandbox) CreateChild(cid, bundleDir string) error { - log.Debugf("Create non-root container sandbox %q, pid: %d for container %q with bundle directory %q", s.ID, s.Pid, cid, bundleDir) - - // Connect to the gofer and prepare it to serve from bundleDir for this - // container. - goferConn, err := s.goferConnect() - if err != nil { - return fmt.Errorf("couldn't connect to gofer: %v", err) - } - defer goferConn.Close() - goferReq := fsgofer.AddBundleDirsRequest{BundleDirs: map[string]string{cid: bundleDir}} - if err := goferConn.Call(fsgofer.AddBundleDirs, &goferReq, nil); err != nil { - return fmt.Errorf("error serving new filesystem for non-root container %v: %v", goferReq, err) +// Start starts running a non-root container inside the sandbox. +func (s *Sandbox) Start(spec *specs.Spec, conf *boot.Config, cid string, ioFiles []*os.File) error { + for _, f := range ioFiles { + defer f.Close() } - return nil -} - -// Start starts running a non-root container inside the sandbox. -func (s *Sandbox) Start(spec *specs.Spec, conf *boot.Config, cid string) error { log.Debugf("Start non-root container sandbox %q, pid: %d", s.ID, s.Pid) - sandboxConn, err := s.sandboxConnect() if err != nil { return fmt.Errorf("couldn't connect to sandbox: %v", err) } defer sandboxConn.Close() - goferConn, err := s.goferConnect() - if err != nil { - return fmt.Errorf("couldn't connect to gofer: %v", err) - } - defer goferConn.Close() - - // Create socket that connects the sandbox and gofer. - sandEnd, goferEnd, err := createSocketPair() - if err != nil { - return err - } - defer sandEnd.Close() - defer goferEnd.Close() - - // Tell the Gofer about the new filesystem it needs to serve. - goferReq := fsgofer.ServeDirectoryRequest{ - Dir: spec.Root.Path, - IsReadOnly: spec.Root.Readonly, - CID: cid, - FilePayload: urpc.FilePayload{Files: []*os.File{goferEnd}}, - } - if err := goferConn.Call(fsgofer.ServeDirectory, &goferReq, nil); err != nil { - return fmt.Errorf("error serving new filesystem for non-root container %v: %v", goferReq, err) - } // Start running the container. args := boot.StartArgs{ Spec: spec, Conf: conf, CID: cid, - FilePayload: urpc.FilePayload{Files: []*os.File{sandEnd}}, + FilePayload: urpc.FilePayload{Files: ioFiles}, } if err := sandboxConn.Call(boot.ContainerStart, &args, nil); err != nil { return fmt.Errorf("error starting non-root container %v: %v", spec.Process.Args, err) } - return nil } @@ -275,102 +222,13 @@ func (s *Sandbox) sandboxConnect() (*urpc.Client, error) { return conn, nil } -func (s *Sandbox) goferConnect() (*urpc.Client, error) { - log.Debugf("Connecting to gofer for sandbox %q", s.ID) - conn, err := client.ConnectTo(fsgofer.ControlSocketAddr(s.ID)) - if err != nil { - return nil, s.connError(err) - } - return conn, nil -} - func (s *Sandbox) connError(err error) error { return fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err) } -func (s *Sandbox) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir, binPath string) (int, []*os.File, error) { - if conf.FileAccess == boot.FileAccessDirect { - // Don't start a gofer. The sandbox will access host FS directly. - return 0, nil, nil - } - - // Start with the general config flags. - args := conf.ToFlags() - args = append(args, "gofer", "--bundle", bundleDir) - - // Add root mount and then add any other additional mounts. - mountCount := 1 - - // Add additional mounts. - for _, m := range spec.Mounts { - if specutils.Is9PMount(m) { - mountCount++ - } - } - sandEnds := make([]*os.File, 0, mountCount) - goferEnds := make([]*os.File, 0, mountCount) - // nextFD is the next available file descriptor for the gofer process. - // It starts at 3 because 0-2 are used by stdin/stdout/stderr. - var nextFD int - for nextFD = 3; nextFD-3 < mountCount; nextFD++ { - sandEnd, goferEnd, err := createSocketPair() - if err != nil { - return 0, nil, err - } - defer goferEnd.Close() - sandEnds = append(sandEnds, sandEnd) - goferEnds = append(goferEnds, goferEnd) - args = append(args, fmt.Sprintf("--io-fds=%d", nextFD)) - } - - // Create and donate a file descriptor for the control server. - addr := fsgofer.ControlSocketAddr(s.ID) - serverFD, err := server.CreateSocket(addr) - if err != nil { - return 0, nil, fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) - } - - // Add the control server fd. - args = append(args, "--controller-fd="+strconv.Itoa(nextFD)) - nextFD++ - controllerFile := os.NewFile(uintptr(serverFD), "gofer_control_socket_server") - defer controllerFile.Close() - - cmd := exec.Command(binPath, args...) - cmd.ExtraFiles = goferEnds - cmd.ExtraFiles = append(cmd.ExtraFiles, controllerFile) - - // Setup any uid/gid mappings, and create or join the configured user - // namespace so the gofer's view of the filesystem aligns with the - // users in the sandbox. - setUIDGIDMappings(cmd, spec) - nss := filterNS([]specs.LinuxNamespaceType{specs.UserNamespace}, spec) - - if conf.Overlay { - args = append(args, "--panic-on-write=true") - } - - // Start the gofer in the given namespace. - log.Debugf("Starting gofer: %s %v", binPath, args) - if err := startInNS(cmd, nss); err != nil { - return 0, nil, err - } - log.Infof("Gofer started, pid: %d", cmd.Process.Pid) - return cmd.Process.Pid, sandEnds, nil -} - -// createSocketPair creates a pair of files wrapping a socket pair. -func createSocketPair() (*os.File, *os.File, error) { - fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) - if err != nil { - return nil, nil, err - } - return os.NewFile(uintptr(fds[0]), "sandbox io fd"), os.NewFile(uintptr(fds[1]), "gofer io fd"), nil -} - // createSandboxProcess starts the sandbox as a subprocess by running the "boot" // command, passing in the bundle dir. -func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, binPath string, ioFiles []*os.File) error { +func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string, ioFiles []*os.File) error { // nextFD is used to get unused FDs that we can pass to the sandbox. It // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 @@ -387,6 +245,10 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund consoleEnabled := consoleSocket != "" + binPath, err := specutils.BinPath() + if err != nil { + return err + } cmd := exec.Command(binPath, conf.ToFlags()...) cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.Args = append(cmd.Args, @@ -464,7 +326,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // Joins the network namespace if network is enabled. the sandbox talks // directly to the host network, which may have been configured in the // namespace. - if ns, ok := getNS(specs.NetworkNamespace, spec); ok && conf.Network != boot.NetworkNone { + if ns, ok := specutils.GetNS(specs.NetworkNamespace, spec); ok && conf.Network != boot.NetworkNone { log.Infof("Sandbox will be started in the container's network namespace: %+v", ns) nss = append(nss, ns) } else { @@ -478,10 +340,10 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // - Gofer: when using a Gofer, the sandbox process can run isolated in an // empty namespace. if conf.Network == boot.NetworkHost || conf.FileAccess == boot.FileAccessDirect { - if userns, ok := getNS(specs.UserNamespace, spec); ok { + if userns, ok := specutils.GetNS(specs.UserNamespace, spec); ok { log.Infof("Sandbox will be started in container's user namespace: %+v", userns) nss = append(nss, userns) - setUIDGIDMappings(cmd, spec) + specutils.SetUIDGIDMappings(cmd, spec) } else { log.Infof("Sandbox will be started in the current user namespace") } @@ -496,7 +358,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } log.Debugf("Starting sandbox: %s %v", binPath, cmd.Args) - if err := startInNS(cmd, nss); err != nil { + if err := specutils.StartInNS(cmd, nss); err != nil { return err } s.Pid = cmd.Process.Pid diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index a22ab789a..97a504b20 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -4,7 +4,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "specutils", - srcs = ["specutils.go"], + srcs = [ + "namespace.go", + "specutils.go", + ], importpath = "gvisor.googlesource.com/gvisor/runsc/specutils", visibility = [ "//runsc:__subpackages__", @@ -15,6 +18,7 @@ go_library( "//pkg/sentry/kernel/auth", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go new file mode 100644 index 000000000..80eaad965 --- /dev/null +++ b/runsc/specutils/namespace.go @@ -0,0 +1,204 @@ +// Copyright 2018 Google Inc. +// +// 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 specutils + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "syscall" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" + "gvisor.googlesource.com/gvisor/pkg/log" +) + +// nsCloneFlag returns the clone flag that can be used to set a namespace of +// the given type. +func nsCloneFlag(nst specs.LinuxNamespaceType) uintptr { + switch nst { + case specs.IPCNamespace: + return syscall.CLONE_NEWIPC + case specs.MountNamespace: + return syscall.CLONE_NEWNS + case specs.NetworkNamespace: + return syscall.CLONE_NEWNET + case specs.PIDNamespace: + return syscall.CLONE_NEWPID + case specs.UTSNamespace: + return syscall.CLONE_NEWUTS + case specs.UserNamespace: + return syscall.CLONE_NEWUSER + case specs.CgroupNamespace: + panic("cgroup namespace has no associated clone flag") + default: + panic(fmt.Sprintf("unknown namespace %v", nst)) + } +} + +// nsPath returns the path of the namespace for the current process and the +// given namespace. +func nsPath(nst specs.LinuxNamespaceType) string { + base := "/proc/self/ns" + switch nst { + case specs.CgroupNamespace: + return filepath.Join(base, "cgroup") + case specs.IPCNamespace: + return filepath.Join(base, "ipc") + case specs.MountNamespace: + return filepath.Join(base, "mnt") + case specs.NetworkNamespace: + return filepath.Join(base, "net") + case specs.PIDNamespace: + return filepath.Join(base, "pid") + case specs.UserNamespace: + return filepath.Join(base, "user") + case specs.UTSNamespace: + return filepath.Join(base, "uts") + default: + panic(fmt.Sprintf("unknown namespace %v", nst)) + } +} + +// GetNS returns true and the namespace with the given type from the slice of +// namespaces in the spec. It returns false if the slice does not contain a +// namespace with the type. +func GetNS(nst specs.LinuxNamespaceType, s *specs.Spec) (specs.LinuxNamespace, bool) { + if s.Linux == nil { + return specs.LinuxNamespace{}, false + } + for _, ns := range s.Linux.Namespaces { + if ns.Type == nst { + return ns, true + } + } + return specs.LinuxNamespace{}, false +} + +// FilterNS returns a slice of namespaces from the spec with types that match +// those in the `filter` slice. +func FilterNS(filter []specs.LinuxNamespaceType, s *specs.Spec) []specs.LinuxNamespace { + if s.Linux == nil { + return nil + } + var out []specs.LinuxNamespace + for _, nst := range filter { + if ns, ok := GetNS(nst, s); ok { + out = append(out, ns) + } + } + return out +} + +// SetNS sets the namespace of the given type. It must be called with +// OSThreadLocked. +func SetNS(fd, nsType uintptr) error { + if _, _, err := syscall.RawSyscall(unix.SYS_SETNS, fd, nsType, 0); err != 0 { + return err + } + return nil +} + +// ApplyNS applies the namespace on the current thread and returns a function +// that will restore the namespace to the original value. +// +// Preconditions: Must be called with os thread locked. +func ApplyNS(ns specs.LinuxNamespace) (func(), error) { + log.Infof("applying namespace %v at path %q", ns.Type, ns.Path) + newNS, err := os.Open(ns.Path) + if err != nil { + return nil, fmt.Errorf("error opening %q: %v", ns.Path, err) + } + defer newNS.Close() + + // Store current netns to restore back after child is started. + curPath := nsPath(ns.Type) + oldNS, err := os.Open(curPath) + if err != nil { + return nil, fmt.Errorf("error opening %q: %v", curPath, err) + } + + // Set netns to the one requested and setup function to restore it back. + flag := nsCloneFlag(ns.Type) + if err := SetNS(newNS.Fd(), flag); err != nil { + oldNS.Close() + return nil, fmt.Errorf("error setting namespace of type %v and path %q: %v", ns.Type, ns.Path, err) + } + return func() { + log.Infof("restoring namespace %v", ns.Type) + defer oldNS.Close() + if err := SetNS(oldNS.Fd(), flag); err != nil { + panic(fmt.Sprintf("error restoring namespace: of type %v: %v", ns.Type, err)) + } + }, nil +} + +// StartInNS joins or creates the given namespaces and calls cmd.Start before +// restoring the namespaces to the original values. +func StartInNS(cmd *exec.Cmd, nss []specs.LinuxNamespace) error { + // We are about to setup namespaces, which requires the os thread being + // locked so that Go doesn't change the thread out from under us. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + + for _, ns := range nss { + if ns.Path == "" { + // No path. Just set a flag to create a new namespace. + cmd.SysProcAttr.Cloneflags |= nsCloneFlag(ns.Type) + continue + } + // Join the given namespace, and restore the current namespace + // before exiting. + restoreNS, err := ApplyNS(ns) + if err != nil { + return err + } + defer restoreNS() + } + + return cmd.Start() +} + +// SetUIDGIDMappings sets the given uid/gid mappings from the spec on the cmd. +func SetUIDGIDMappings(cmd *exec.Cmd, s *specs.Spec) { + if s.Linux == nil { + return + } + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + for _, idMap := range s.Linux.UIDMappings { + log.Infof("Mapping host uid %d to container uid %d (size=%d)", idMap.HostID, idMap.ContainerID, idMap.Size) + cmd.SysProcAttr.UidMappings = append(cmd.SysProcAttr.UidMappings, syscall.SysProcIDMap{ + ContainerID: int(idMap.ContainerID), + HostID: int(idMap.HostID), + Size: int(idMap.Size), + }) + } + for _, idMap := range s.Linux.GIDMappings { + log.Infof("Mapping host gid %d to container gid %d (size=%d)", idMap.HostID, idMap.ContainerID, idMap.Size) + cmd.SysProcAttr.GidMappings = append(cmd.SysProcAttr.GidMappings, syscall.SysProcIDMap{ + ContainerID: int(idMap.ContainerID), + HostID: int(idMap.HostID), + Size: int(idMap.Size), + }) + } +} diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index 03ab3c4ac..ca91e07ff 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -18,5 +18,6 @@ go_library( "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", ], ) diff --git a/runsc/test/testutil/docker.go b/runsc/test/testutil/docker.go index b7d60e712..fc67c174a 100644 --- a/runsc/test/testutil/docker.go +++ b/runsc/test/testutil/docker.go @@ -32,7 +32,7 @@ func init() { rand.Seed(time.Now().UnixNano()) } -func runtime() string { +func getRuntime() string { r := os.Getenv("RUNSC_RUNTIME") if r == "" { return "runsc-test" @@ -43,7 +43,7 @@ func runtime() string { // IsPauseResumeSupported returns true if Pause/Resume is supported by runtime. func IsPauseResumeSupported() bool { // Native host network stack can't be saved. - return !strings.Contains(runtime(), "hostnet") + return !strings.Contains(getRuntime(), "hostnet") } // EnsureSupportedDockerVersion checks if correct docker is installed. @@ -128,7 +128,7 @@ type Docker struct { // Names of containers will be unique. func MakeDocker(namePrefix string) Docker { suffix := fmt.Sprintf("-%06d", rand.Int())[:7] - return Docker{Name: namePrefix + suffix, Runtime: runtime()} + return Docker{Name: namePrefix + suffix, Runtime: getRuntime()} } // Create calls 'docker create' with the arguments provided. diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index fc3d61e52..e90ab5ad5 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -23,11 +23,16 @@ import ( "io/ioutil" "net/http" "os" + "os/exec" "path/filepath" + "runtime" + "syscall" + "testing" "time" "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -227,3 +232,55 @@ func WaitForHTTP(port int, timeout time.Duration) error { } return Poll(cb, timeout) } + +// RunAsRoot ensures the test runs with CAP_SYS_ADMIN. If need it will create +// a new user namespace and reexecute the test as root inside of the namespace. +func RunAsRoot(m *testing.M) { + caps, err := capability.NewPid2(os.Getpid()) + if err != nil { + panic(err.Error()) + } + if err := caps.Load(); err != nil { + panic(err.Error()) + } + if caps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) { + // Capability: check! Good to run. + os.Exit(m.Run()) + } + + // Current process doesn't have CAP_SYS_ADMIN, create user namespace and run + // as root inside that namespace to get it. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + cmd := exec.Command("/proc/self/exe", os.Args...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS, + // Set current user/group as root inside the namespace. + UidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: os.Getuid(), Size: 1}, + }, + GidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: os.Getgid(), Size: 1}, + }, + GidMappingsEnableSetgroups: false, + Credential: &syscall.Credential{ + Uid: 0, + Gid: 0, + }, + } + cmd.Env = os.Environ() + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + if exit, ok := err.(*exec.ExitError); ok { + if ws, ok := exit.Sys().(syscall.WaitStatus); ok { + os.Exit(ws.ExitStatus()) + } + os.Exit(-1) + } + panic(fmt.Sprint("error running child process:", err.Error())) + } + os.Exit(0) +} -- cgit v1.2.3 From 5ade9350ad18476a2cddbd3a0b36778d1c6ec376 Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Thu, 30 Aug 2018 15:46:12 -0700 Subject: runsc: Pass log and config files to sandbox process by FD. This is a prereq for running the sandbox process as user "nobody", when it may not have permissions to open these files. Instead, we must open then before starting the sandbox process, and pass them by FD. PiperOrigin-RevId: 210995199 Change-Id: I715875a9553290b4a49394a8fcd93be78b1933dd --- runsc/boot/config.go | 3 ++ runsc/cmd/boot.go | 15 ++++++--- runsc/cmd/create.go | 3 ++ runsc/cmd/run.go | 3 ++ runsc/main.go | 25 ++++++++------ runsc/sandbox/sandbox.go | 74 +++++++++++++++++++++++++++++++---------- runsc/specutils/specutils.go | 26 ++++++++++++--- runsc/test/testutil/testutil.go | 1 + 8 files changed, 113 insertions(+), 37 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/config.go b/runsc/boot/config.go index efb8563ea..212f5b003 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -204,6 +204,9 @@ type Config struct { // TODO: Remove this when multiple container is fully supported. MultiContainer bool + // SpecFile is the file containing the OCI spec. + SpecFile string + // WatchdogAction sets what action the watchdog takes when triggered. WatchdogAction watchdog.Action diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 4e08dafc8..4bd6fa12a 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -32,9 +32,12 @@ import ( // Boot implements subcommands.Command for the "boot" command which starts a // new sandbox. It should not be called directly. type Boot struct { - // bundleDir is the path to the bundle directory. + // bundleDir is the directory containing the OCI spec. bundleDir string + // specFD is the file descriptor that the spec will be read from. + specFD int + // controllerFD is the file descriptor of a stream socket for the // control server that is donated to this process. controllerFD int @@ -68,7 +71,7 @@ func (*Boot) Usage() string { // SetFlags implements subcommands.Command.SetFlags. func (b *Boot) SetFlags(f *flag.FlagSet) { - f.StringVar(&b.bundleDir, "bundle", "", "required path to the root of the bundle directory") + f.IntVar(&b.specFD, "spec-fd", -1, "required fd with the container spec") f.IntVar(&b.controllerFD, "controller-fd", -1, "required FD of a stream socket for the control server that must be donated to this process") f.Var(&b.ioFDs, "io-fds", "list of FDs to connect 9P clients. They must follow this order: root first, then mounts as defined in the spec") f.BoolVar(&b.console, "console", false, "set to true if the sandbox should allow terminal ioctl(2) syscalls") @@ -78,7 +81,7 @@ func (b *Boot) SetFlags(f *flag.FlagSet) { // Execute implements subcommands.Command.Execute. It starts a sandbox in a // waiting state. func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { - if b.bundleDir == "" || b.controllerFD == -1 || f.NArg() != 0 { + if b.specFD == -1 || b.controllerFD == -1 || f.NArg() != 0 { f.Usage() return subcommands.ExitUsageError } @@ -86,8 +89,10 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) // Ensure that if there is a panic, all goroutine stacks are printed. debug.SetTraceback("all") - // Get the spec from the bundleDir. - spec, err := specutils.ReadSpec(b.bundleDir) + // Get the spec from the specFD. + specFile := os.NewFile(uintptr(b.specFD), "spec file") + defer specFile.Close() + spec, err := specutils.ReadSpecFromFile(specFile) if err != nil { Fatalf("error reading spec: %v", err) } diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 94a889077..38ae03e7a 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -15,6 +15,8 @@ package cmd import ( + "path/filepath" + "context" "flag" "github.com/google/subcommands" @@ -83,6 +85,7 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} Fatalf("error reading spec: %v", err) } specutils.LogSpec(spec) + conf.SpecFile = filepath.Join(bundleDir, "config.json") // Create the container. A new sandbox will be created for the // container unless the metadata specifies that it should be run in an diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index 681112f30..92aa6bc40 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -15,6 +15,7 @@ package cmd import ( + "path/filepath" "syscall" "context" @@ -71,6 +72,8 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s if err != nil { Fatalf("error reading spec: %v", err) } + specutils.LogSpec(spec) + conf.SpecFile = filepath.Join(bundleDir, "config.json") ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile) if err != nil { diff --git a/runsc/main.go b/runsc/main.go index 773ec6486..0c9b9af78 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -17,13 +17,11 @@ package main import ( - "fmt" "io" "os" "path/filepath" "strings" "syscall" - "time" "context" "flag" @@ -32,6 +30,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/cmd" + "gvisor.googlesource.com/gvisor/runsc/specutils" ) var ( @@ -48,6 +47,8 @@ var ( // Debugging flags. debugLogDir = flag.String("debug-log-dir", "", "additional location for logs. It creates individual log files per command") logPackets = flag.Bool("log-packets", false, "enable network packet logging") + logFD = flag.Int("log-fd", -1, "file descriptor to log to. If set, the 'log' flag is ignored.") + debugLogFD = flag.Int("debug-log-fd", -1, "file descriptor to write debug logs to. If set, the 'debug-log-dir' flag is ignored.") // Debugging flags: strace related strace = flag.Bool("strace", false, "enable strace") @@ -64,6 +65,7 @@ var ( panicSignal = flag.Int("panic-signal", -1, "register signal handling that panics. Usually set to SIGUSR2(12) to troubleshoot hangs. -1 disables it.") ) +// gitRevision is set during linking. var gitRevision = "" func main() { @@ -152,7 +154,9 @@ func main() { } var logFile io.Writer = os.Stderr - if *logFilename != "" { + if *logFD > -1 { + logFile = os.NewFile(uintptr(*logFD), "log file") + } else if *logFilename != "" { // We must set O_APPEND and not O_TRUNC because Docker passes // the same log file for all commands (and also parses these // log files), so we can't destroy them on each command. @@ -173,18 +177,17 @@ func main() { cmd.Fatalf("invalid log format %q, must be 'json' or 'text'", *logFormat) } - if *debugLogDir != "" { + if *debugLogFD > -1 { + f := os.NewFile(uintptr(*debugLogFD), "debug log file") + e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} + } else if *debugLogDir != "" { if err := os.MkdirAll(*debugLogDir, 0775); err != nil { cmd.Fatalf("error creating dir %q: %v", *debugLogDir, err) } - - // Format: /runsc.log.. - scmd := flag.CommandLine.Arg(0) - filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), scmd) - path := filepath.Join(*debugLogDir, filename) - f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) + subcommand := flag.CommandLine.Arg(0) + f, err := specutils.DebugLogFile(*debugLogDir, subcommand) if err != nil { - cmd.Fatalf("error opening log file %q: %v", filename, err) + cmd.Fatalf("error opening debug log file in %q: %v", *debugLogDir, err) } e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index f14a2f8c9..f58916574 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -233,16 +233,6 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 - // Create control server socket here and donate FD to child process because - // it may be in a different network namespace and won't be reachable from - // outside. - addr := boot.ControlSocketAddr(s.ID) - fd, err := server.CreateSocket(addr) - log.Infof("Creating sandbox process with addr: %s", addr[1:]) // skip "\00". - if err != nil { - return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) - } - consoleEnabled := consoleSocket != "" binPath, err := specutils.BinPath() @@ -251,16 +241,61 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } cmd := exec.Command(binPath, conf.ToFlags()...) cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.Args = append(cmd.Args, - "boot", - "--bundle", bundleDir, - "--controller-fd="+strconv.Itoa(nextFD), - "--console="+strconv.FormatBool(consoleEnabled)) - nextFD++ - controllerFile := os.NewFile(uintptr(fd), "control_server_socket") + // Open the log files to pass to the sandbox as FDs. + // + // These flags must come BEFORE the "boot" command in cmd.Args. + if conf.LogFilename != "" { + logFile, err := os.OpenFile(conf.LogFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("error opening log file %q: %v", conf.LogFilename, err) + } + defer logFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, logFile) + cmd.Args = append(cmd.Args, "--log-fd="+strconv.Itoa(nextFD)) + nextFD++ + } + if conf.DebugLogDir != "" { + debugLogFile, err := specutils.DebugLogFile(conf.DebugLogDir, "boot") + if err != nil { + return fmt.Errorf("error opening debug log file in %q: %v", conf.DebugLogDir, err) + } + defer debugLogFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, debugLogFile) + cmd.Args = append(cmd.Args, "--debug-log-fd="+strconv.Itoa(nextFD)) + nextFD++ + } + + // Add the "boot" command to the args. + // + // All flags after this must be for the boot command + cmd.Args = append(cmd.Args, "boot", "--console="+strconv.FormatBool(consoleEnabled)) + + // Create a socket for the control server and donate it to the sandbox. + addr := boot.ControlSocketAddr(s.ID) + sockFD, err := server.CreateSocket(addr) + log.Infof("Creating sandbox process with addr: %s", addr[1:]) // skip "\00". + if err != nil { + return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) + } + controllerFile := os.NewFile(uintptr(sockFD), "control_server_socket") defer controllerFile.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, controllerFile) + cmd.Args = append(cmd.Args, "--controller-fd="+strconv.Itoa(nextFD)) + nextFD++ + + // Open the spec file to donate to the sandbox. + if conf.SpecFile == "" { + return fmt.Errorf("conf.SpecFile must be set") + } + specFile, err := os.Open(conf.SpecFile) + if err != nil { + return fmt.Errorf("error opening spec file %q: %v", conf.SpecFile, err) + } + defer specFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, specFile) + cmd.Args = append(cmd.Args, "--spec-fd="+strconv.Itoa(nextFD)) + nextFD++ // If there is a gofer, sends all socket ends to the sandbox. for _, f := range ioFiles { @@ -357,6 +392,11 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace}) } + // Log the fds we are donating to the sandbox process. + for i, f := range cmd.ExtraFiles { + log.Debugf("Donating FD %d: %q", i+3, f.Name()) + } + log.Debugf("Starting sandbox: %s %v", binPath, cmd.Args) if err := specutils.StartInNS(cmd, nss); err != nil { return err diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 5fb53edb2..477409112 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -108,14 +108,24 @@ func ValidateSpec(spec *specs.Spec) error { // ReadSpec reads an OCI runtime spec from the given bundle directory. func ReadSpec(bundleDir string) (*specs.Spec, error) { // The spec file must be in "config.json" inside the bundle directory. - specFile := filepath.Join(bundleDir, "config.json") - specBytes, err := ioutil.ReadFile(specFile) + specPath := filepath.Join(bundleDir, "config.json") + specFile, err := os.Open(specPath) if err != nil { - return nil, fmt.Errorf("error reading spec from file %q: %v", specFile, err) + return nil, fmt.Errorf("error opening spec file %q: %v", specPath, err) + } + defer specFile.Close() + return ReadSpecFromFile(specFile) +} + +// ReadSpecFromFile reads an OCI runtime spec from the given File. +func ReadSpecFromFile(specFile *os.File) (*specs.Spec, error) { + specBytes, err := ioutil.ReadAll(specFile) + if err != nil { + return nil, fmt.Errorf("error reading spec from file %q: %v", specFile.Name(), err) } var spec specs.Spec if err := json.Unmarshal(specBytes, &spec); err != nil { - return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile, err, string(specBytes)) + return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile.Name(), err, string(specBytes)) } if err := ValidateSpec(&spec); err != nil { return nil, err @@ -346,3 +356,11 @@ func WaitForReady(pid int, timeout time.Duration, ready func() (bool, error)) er } return backoff.Retry(op, b) } + +// DebugLogFile opens a file in logDir based on the timestamp and subcommand +// for writing. +func DebugLogFile(logDir, subcommand string) (*os.File, error) { + // Format: /runsc.log.. + filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), subcommand) + return os.OpenFile(filepath.Join(logDir, filename), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) +} diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index e90ab5ad5..4a1c37105 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -176,6 +176,7 @@ func SetupContainerInRoot(rootDir string, spec *specs.Spec, conf *boot.Config) ( } conf.RootDir = rootDir + conf.SpecFile = filepath.Join(bundleDir, "config.json") return bundleDir, nil } -- cgit v1.2.3 From 7e18f158b2ea87b7f06f8e0b91e10558b4f52722 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Fri, 31 Aug 2018 11:29:36 -0700 Subject: Automated rollback of changelist 210995199 PiperOrigin-RevId: 211116429 Change-Id: I446d149c822177dc9fc3c64ce5e455f7f029aa82 --- runsc/boot/config.go | 3 -- runsc/cmd/boot.go | 15 +++------ runsc/cmd/create.go | 3 -- runsc/cmd/run.go | 3 -- runsc/main.go | 25 ++++++-------- runsc/sandbox/sandbox.go | 74 ++++++++++------------------------------- runsc/specutils/specutils.go | 26 +++------------ runsc/test/testutil/testutil.go | 1 - 8 files changed, 37 insertions(+), 113 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/config.go b/runsc/boot/config.go index 212f5b003..efb8563ea 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -204,9 +204,6 @@ type Config struct { // TODO: Remove this when multiple container is fully supported. MultiContainer bool - // SpecFile is the file containing the OCI spec. - SpecFile string - // WatchdogAction sets what action the watchdog takes when triggered. WatchdogAction watchdog.Action diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 4bd6fa12a..4e08dafc8 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -32,12 +32,9 @@ import ( // Boot implements subcommands.Command for the "boot" command which starts a // new sandbox. It should not be called directly. type Boot struct { - // bundleDir is the directory containing the OCI spec. + // bundleDir is the path to the bundle directory. bundleDir string - // specFD is the file descriptor that the spec will be read from. - specFD int - // controllerFD is the file descriptor of a stream socket for the // control server that is donated to this process. controllerFD int @@ -71,7 +68,7 @@ func (*Boot) Usage() string { // SetFlags implements subcommands.Command.SetFlags. func (b *Boot) SetFlags(f *flag.FlagSet) { - f.IntVar(&b.specFD, "spec-fd", -1, "required fd with the container spec") + f.StringVar(&b.bundleDir, "bundle", "", "required path to the root of the bundle directory") f.IntVar(&b.controllerFD, "controller-fd", -1, "required FD of a stream socket for the control server that must be donated to this process") f.Var(&b.ioFDs, "io-fds", "list of FDs to connect 9P clients. They must follow this order: root first, then mounts as defined in the spec") f.BoolVar(&b.console, "console", false, "set to true if the sandbox should allow terminal ioctl(2) syscalls") @@ -81,7 +78,7 @@ func (b *Boot) SetFlags(f *flag.FlagSet) { // Execute implements subcommands.Command.Execute. It starts a sandbox in a // waiting state. func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { - if b.specFD == -1 || b.controllerFD == -1 || f.NArg() != 0 { + if b.bundleDir == "" || b.controllerFD == -1 || f.NArg() != 0 { f.Usage() return subcommands.ExitUsageError } @@ -89,10 +86,8 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) // Ensure that if there is a panic, all goroutine stacks are printed. debug.SetTraceback("all") - // Get the spec from the specFD. - specFile := os.NewFile(uintptr(b.specFD), "spec file") - defer specFile.Close() - spec, err := specutils.ReadSpecFromFile(specFile) + // Get the spec from the bundleDir. + spec, err := specutils.ReadSpec(b.bundleDir) if err != nil { Fatalf("error reading spec: %v", err) } diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 38ae03e7a..94a889077 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -15,8 +15,6 @@ package cmd import ( - "path/filepath" - "context" "flag" "github.com/google/subcommands" @@ -85,7 +83,6 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} Fatalf("error reading spec: %v", err) } specutils.LogSpec(spec) - conf.SpecFile = filepath.Join(bundleDir, "config.json") // Create the container. A new sandbox will be created for the // container unless the metadata specifies that it should be run in an diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index 92aa6bc40..681112f30 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -15,7 +15,6 @@ package cmd import ( - "path/filepath" "syscall" "context" @@ -72,8 +71,6 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s if err != nil { Fatalf("error reading spec: %v", err) } - specutils.LogSpec(spec) - conf.SpecFile = filepath.Join(bundleDir, "config.json") ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile) if err != nil { diff --git a/runsc/main.go b/runsc/main.go index 0c9b9af78..773ec6486 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -17,11 +17,13 @@ package main import ( + "fmt" "io" "os" "path/filepath" "strings" "syscall" + "time" "context" "flag" @@ -30,7 +32,6 @@ import ( "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/cmd" - "gvisor.googlesource.com/gvisor/runsc/specutils" ) var ( @@ -47,8 +48,6 @@ var ( // Debugging flags. debugLogDir = flag.String("debug-log-dir", "", "additional location for logs. It creates individual log files per command") logPackets = flag.Bool("log-packets", false, "enable network packet logging") - logFD = flag.Int("log-fd", -1, "file descriptor to log to. If set, the 'log' flag is ignored.") - debugLogFD = flag.Int("debug-log-fd", -1, "file descriptor to write debug logs to. If set, the 'debug-log-dir' flag is ignored.") // Debugging flags: strace related strace = flag.Bool("strace", false, "enable strace") @@ -65,7 +64,6 @@ var ( panicSignal = flag.Int("panic-signal", -1, "register signal handling that panics. Usually set to SIGUSR2(12) to troubleshoot hangs. -1 disables it.") ) -// gitRevision is set during linking. var gitRevision = "" func main() { @@ -154,9 +152,7 @@ func main() { } var logFile io.Writer = os.Stderr - if *logFD > -1 { - logFile = os.NewFile(uintptr(*logFD), "log file") - } else if *logFilename != "" { + if *logFilename != "" { // We must set O_APPEND and not O_TRUNC because Docker passes // the same log file for all commands (and also parses these // log files), so we can't destroy them on each command. @@ -177,17 +173,18 @@ func main() { cmd.Fatalf("invalid log format %q, must be 'json' or 'text'", *logFormat) } - if *debugLogFD > -1 { - f := os.NewFile(uintptr(*debugLogFD), "debug log file") - e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} - } else if *debugLogDir != "" { + if *debugLogDir != "" { if err := os.MkdirAll(*debugLogDir, 0775); err != nil { cmd.Fatalf("error creating dir %q: %v", *debugLogDir, err) } - subcommand := flag.CommandLine.Arg(0) - f, err := specutils.DebugLogFile(*debugLogDir, subcommand) + + // Format: /runsc.log.. + scmd := flag.CommandLine.Arg(0) + filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), scmd) + path := filepath.Join(*debugLogDir, filename) + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) if err != nil { - cmd.Fatalf("error opening debug log file in %q: %v", *debugLogDir, err) + cmd.Fatalf("error opening log file %q: %v", filename, err) } e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index f58916574..f14a2f8c9 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -233,6 +233,16 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 + // Create control server socket here and donate FD to child process because + // it may be in a different network namespace and won't be reachable from + // outside. + addr := boot.ControlSocketAddr(s.ID) + fd, err := server.CreateSocket(addr) + log.Infof("Creating sandbox process with addr: %s", addr[1:]) // skip "\00". + if err != nil { + return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) + } + consoleEnabled := consoleSocket != "" binPath, err := specutils.BinPath() @@ -241,61 +251,16 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } cmd := exec.Command(binPath, conf.ToFlags()...) cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.Args = append(cmd.Args, + "boot", + "--bundle", bundleDir, + "--controller-fd="+strconv.Itoa(nextFD), + "--console="+strconv.FormatBool(consoleEnabled)) + nextFD++ - // Open the log files to pass to the sandbox as FDs. - // - // These flags must come BEFORE the "boot" command in cmd.Args. - if conf.LogFilename != "" { - logFile, err := os.OpenFile(conf.LogFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return fmt.Errorf("error opening log file %q: %v", conf.LogFilename, err) - } - defer logFile.Close() - cmd.ExtraFiles = append(cmd.ExtraFiles, logFile) - cmd.Args = append(cmd.Args, "--log-fd="+strconv.Itoa(nextFD)) - nextFD++ - } - if conf.DebugLogDir != "" { - debugLogFile, err := specutils.DebugLogFile(conf.DebugLogDir, "boot") - if err != nil { - return fmt.Errorf("error opening debug log file in %q: %v", conf.DebugLogDir, err) - } - defer debugLogFile.Close() - cmd.ExtraFiles = append(cmd.ExtraFiles, debugLogFile) - cmd.Args = append(cmd.Args, "--debug-log-fd="+strconv.Itoa(nextFD)) - nextFD++ - } - - // Add the "boot" command to the args. - // - // All flags after this must be for the boot command - cmd.Args = append(cmd.Args, "boot", "--console="+strconv.FormatBool(consoleEnabled)) - - // Create a socket for the control server and donate it to the sandbox. - addr := boot.ControlSocketAddr(s.ID) - sockFD, err := server.CreateSocket(addr) - log.Infof("Creating sandbox process with addr: %s", addr[1:]) // skip "\00". - if err != nil { - return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) - } - controllerFile := os.NewFile(uintptr(sockFD), "control_server_socket") + controllerFile := os.NewFile(uintptr(fd), "control_server_socket") defer controllerFile.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, controllerFile) - cmd.Args = append(cmd.Args, "--controller-fd="+strconv.Itoa(nextFD)) - nextFD++ - - // Open the spec file to donate to the sandbox. - if conf.SpecFile == "" { - return fmt.Errorf("conf.SpecFile must be set") - } - specFile, err := os.Open(conf.SpecFile) - if err != nil { - return fmt.Errorf("error opening spec file %q: %v", conf.SpecFile, err) - } - defer specFile.Close() - cmd.ExtraFiles = append(cmd.ExtraFiles, specFile) - cmd.Args = append(cmd.Args, "--spec-fd="+strconv.Itoa(nextFD)) - nextFD++ // If there is a gofer, sends all socket ends to the sandbox. for _, f := range ioFiles { @@ -392,11 +357,6 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace}) } - // Log the fds we are donating to the sandbox process. - for i, f := range cmd.ExtraFiles { - log.Debugf("Donating FD %d: %q", i+3, f.Name()) - } - log.Debugf("Starting sandbox: %s %v", binPath, cmd.Args) if err := specutils.StartInNS(cmd, nss); err != nil { return err diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 477409112..5fb53edb2 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -108,24 +108,14 @@ func ValidateSpec(spec *specs.Spec) error { // ReadSpec reads an OCI runtime spec from the given bundle directory. func ReadSpec(bundleDir string) (*specs.Spec, error) { // The spec file must be in "config.json" inside the bundle directory. - specPath := filepath.Join(bundleDir, "config.json") - specFile, err := os.Open(specPath) + specFile := filepath.Join(bundleDir, "config.json") + specBytes, err := ioutil.ReadFile(specFile) if err != nil { - return nil, fmt.Errorf("error opening spec file %q: %v", specPath, err) - } - defer specFile.Close() - return ReadSpecFromFile(specFile) -} - -// ReadSpecFromFile reads an OCI runtime spec from the given File. -func ReadSpecFromFile(specFile *os.File) (*specs.Spec, error) { - specBytes, err := ioutil.ReadAll(specFile) - if err != nil { - return nil, fmt.Errorf("error reading spec from file %q: %v", specFile.Name(), err) + return nil, fmt.Errorf("error reading spec from file %q: %v", specFile, err) } var spec specs.Spec if err := json.Unmarshal(specBytes, &spec); err != nil { - return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile.Name(), err, string(specBytes)) + return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile, err, string(specBytes)) } if err := ValidateSpec(&spec); err != nil { return nil, err @@ -356,11 +346,3 @@ func WaitForReady(pid int, timeout time.Duration, ready func() (bool, error)) er } return backoff.Retry(op, b) } - -// DebugLogFile opens a file in logDir based on the timestamp and subcommand -// for writing. -func DebugLogFile(logDir, subcommand string) (*os.File, error) { - // Format: /runsc.log.. - filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), subcommand) - return os.OpenFile(filepath.Join(logDir, filename), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) -} diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 4429b981b..25987d040 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -176,7 +176,6 @@ func SetupContainerInRoot(rootDir string, spec *specs.Spec, conf *boot.Config) ( } conf.RootDir = rootDir - conf.SpecFile = filepath.Join(bundleDir, "config.json") return bundleDir, nil } -- cgit v1.2.3 From 9ae4e28f75979905a6396962a232e217323499f9 Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Tue, 4 Sep 2018 13:36:26 -0700 Subject: runsc: fix container rootfs path. PiperOrigin-RevId: 211515350 Change-Id: Ia495af57447c799909aa97bb873a50b87bee2625 --- runsc/cmd/boot.go | 8 -------- runsc/cmd/gofer.go | 6 +----- runsc/cmd/path.go | 10 ---------- runsc/container/fs.go | 6 ------ runsc/specutils/specutils.go | 26 ++++++++++++++++++++++++++ runsc/specutils/specutils_test.go | 22 ++++++++++++++++++++++ 6 files changed, 49 insertions(+), 29 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 4e08dafc8..666be902a 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -93,14 +93,6 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } specutils.LogSpec(spec) - // Turn any relative paths in the spec to absolute by prepending the bundleDir. - spec.Root.Path = absPath(b.bundleDir, spec.Root.Path) - for _, m := range spec.Mounts { - if m.Source != "" { - m.Source = absPath(b.bundleDir, m.Source) - } - } - conf := args[0].(*boot.Config) waitStatus := args[1].(*syscall.WaitStatus) diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index f28e02798..95926f5f9 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -16,7 +16,6 @@ package cmd import ( "os" - "path" "sync" "syscall" @@ -108,7 +107,7 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) syscall.Umask(0) // Find what path is going to be served by this gofer. - root := absPath(g.bundleDir, spec.Root.Path) + root := spec.Root.Path if err := syscall.Chroot(root); err != nil { Fatalf("failed to chroot to %q: %v", root, err) } @@ -131,9 +130,6 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) mountIdx := 1 // first one is the root for _, m := range spec.Mounts { if specutils.Is9PMount(m) { - if !path.IsAbs(m.Destination) { - Fatalf("destination must be absolute path: %v", m.Destination) - } cfg := fsgofer.Config{ ROMount: isReadonlyMount(m.Options), PanicOnWrite: g.panicOnWrite, diff --git a/runsc/cmd/path.go b/runsc/cmd/path.go index 4bb1dbb4f..c207b80da 100644 --- a/runsc/cmd/path.go +++ b/runsc/cmd/path.go @@ -16,18 +16,8 @@ package cmd import ( "os" - "path/filepath" ) -// absPath turns the given path into an absolute path (if it is not already -// absolute) by prepending the base path. -func absPath(base, rel string) string { - if filepath.IsAbs(rel) { - return rel - } - return filepath.Join(base, rel) -} - // getwdOrDie returns the current working directory and dies if it cannot. func getwdOrDie() string { wd, err := os.Getwd() diff --git a/runsc/container/fs.go b/runsc/container/fs.go index b93c866ea..fb352fc7c 100644 --- a/runsc/container/fs.go +++ b/runsc/container/fs.go @@ -78,9 +78,6 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { continue } src := m.Source - if !filepath.IsAbs(src) { - src = filepath.Join(bundleDir, src) - } srcfi, err := os.Stat(src) if err != nil { return fmt.Errorf("failed to stat() mount source: %v", err) @@ -130,9 +127,6 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { log.Infof("Remounting root as readonly: %q", spec.Root.Path) flags := uintptr(syscall.MS_BIND | syscall.MS_REMOUNT | syscall.MS_RDONLY | syscall.MS_REC) src := spec.Root.Path - if !filepath.IsAbs(src) { - src = filepath.Join(bundleDir, src) - } if err := syscall.Mount(src, src, "bind", flags, ""); err != nil { return fmt.Errorf("failed to remount root as readonly with source: %q, target: %q, flags: %#x, err: %v", spec.Root.Path, spec.Root.Path, flags, err) } diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 5fb53edb2..6c1ac56c3 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" "os" + "path" "path/filepath" "strings" "syscall" @@ -83,6 +84,12 @@ func ValidateSpec(spec *specs.Spec) error { log.Warningf("Seccomp spec is being ignored") } + for i, m := range spec.Mounts { + if !path.IsAbs(m.Destination) { + return fmt.Errorf("Spec.Mounts[%d] Mount.Destination must be an absolute path: %v", i, m) + } + } + // Two annotations are use by containerd to support multi-container pods. // "io.kubernetes.cri.container-type" // "io.kubernetes.cri.sandbox-id" @@ -105,7 +112,18 @@ func ValidateSpec(spec *specs.Spec) error { return nil } +// absPath turns the given path into an absolute path (if it is not already +// absolute) by prepending the base path. +func absPath(base, rel string) string { + if filepath.IsAbs(rel) { + return rel + } + return filepath.Join(base, rel) +} + // ReadSpec reads an OCI runtime spec from the given bundle directory. +// ReadSpec also normalizes all potential relative paths into absolute +// path, e.g. spec.Root.Path, mount.Source. func ReadSpec(bundleDir string) (*specs.Spec, error) { // The spec file must be in "config.json" inside the bundle directory. specFile := filepath.Join(bundleDir, "config.json") @@ -120,6 +138,14 @@ func ReadSpec(bundleDir string) (*specs.Spec, error) { if err := ValidateSpec(&spec); err != nil { return nil, err } + // Turn any relative paths in the spec to absolute by prepending the bundleDir. + spec.Root.Path = absPath(bundleDir, spec.Root.Path) + for i := range spec.Mounts { + m := &spec.Mounts[i] + if m.Source != "" { + m.Source = absPath(bundleDir, m.Source) + } + } return &spec, nil } diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go index 2c4e3e729..64e2172c8 100644 --- a/runsc/specutils/specutils_test.go +++ b/runsc/specutils/specutils_test.go @@ -113,6 +113,12 @@ func TestSpecInvalid(t *testing.T) { Process: &specs.Process{ Args: []string{"/bin/true"}, }, + Mounts: []specs.Mount{ + { + Source: "src", + Destination: "/dst", + }, + }, }, error: "", }, @@ -197,6 +203,22 @@ func TestSpecInvalid(t *testing.T) { }, error: "is not supported", }, + { + name: "relative mount destination", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + Mounts: []specs.Mount{ + { + Source: "src", + Destination: "dst", + }, + }, + }, + error: "must be an absolute path", + }, } { err := ValidateSpec(&test.spec) if len(test.error) == 0 { -- cgit v1.2.3 From ad8648c6343cf2cf3e51a0f58cb053ee303f6ffb Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Tue, 4 Sep 2018 20:08:41 -0700 Subject: runsc: Pass log and config files to sandbox process by FD. This is a prereq for running the sandbox process as user "nobody", when it may not have permissions to open these files. Instead, we must open then before starting the sandbox process, and pass them by FD. The specutils.ReadSpecFromFile method was fixed to always seek to the beginning of the file before reading. This allows Files from the same FD to be read multiple times, as we do in the boot command when the apply-caps flag is set. Tested with --network=host. PiperOrigin-RevId: 211570647 Change-Id: I685be0a290aa7f70731ebdce82ebc0ebcc9d475c --- runsc/BUILD | 2 ++ runsc/boot/config.go | 3 ++ runsc/cmd/boot.go | 19 +++++++--- runsc/cmd/create.go | 3 ++ runsc/cmd/run.go | 3 ++ runsc/main.go | 25 +++++++------ runsc/sandbox/sandbox.go | 79 +++++++++++++++++++++++++++++++---------- runsc/specutils/specutils.go | 30 +++++++++++++--- runsc/test/testutil/testutil.go | 1 + 9 files changed, 127 insertions(+), 38 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/BUILD b/runsc/BUILD index 660cb2a06..e390b7bae 100644 --- a/runsc/BUILD +++ b/runsc/BUILD @@ -16,6 +16,7 @@ go_binary( "//pkg/log", "//runsc/boot", "//runsc/cmd", + "//runsc/specutils", "@com_github_google_subcommands//:go_default_library", ], ) @@ -45,6 +46,7 @@ go_binary( "//pkg/log", "//runsc/boot", "//runsc/cmd", + "//runsc/specutils", "@com_github_google_subcommands//:go_default_library", ], ) diff --git a/runsc/boot/config.go b/runsc/boot/config.go index efb8563ea..212f5b003 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -204,6 +204,9 @@ type Config struct { // TODO: Remove this when multiple container is fully supported. MultiContainer bool + // SpecFile is the file containing the OCI spec. + SpecFile string + // WatchdogAction sets what action the watchdog takes when triggered. WatchdogAction watchdog.Action diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 666be902a..784baf23b 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -32,9 +32,12 @@ import ( // Boot implements subcommands.Command for the "boot" command which starts a // new sandbox. It should not be called directly. type Boot struct { - // bundleDir is the path to the bundle directory. + // bundleDir is the directory containing the OCI spec. bundleDir string + // specFD is the file descriptor that the spec will be read from. + specFD int + // controllerFD is the file descriptor of a stream socket for the // control server that is donated to this process. controllerFD int @@ -69,6 +72,7 @@ func (*Boot) Usage() string { // SetFlags implements subcommands.Command.SetFlags. func (b *Boot) SetFlags(f *flag.FlagSet) { f.StringVar(&b.bundleDir, "bundle", "", "required path to the root of the bundle directory") + f.IntVar(&b.specFD, "spec-fd", -1, "required fd with the container spec") f.IntVar(&b.controllerFD, "controller-fd", -1, "required FD of a stream socket for the control server that must be donated to this process") f.Var(&b.ioFDs, "io-fds", "list of FDs to connect 9P clients. They must follow this order: root first, then mounts as defined in the spec") f.BoolVar(&b.console, "console", false, "set to true if the sandbox should allow terminal ioctl(2) syscalls") @@ -78,7 +82,7 @@ func (b *Boot) SetFlags(f *flag.FlagSet) { // Execute implements subcommands.Command.Execute. It starts a sandbox in a // waiting state. func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { - if b.bundleDir == "" || b.controllerFD == -1 || f.NArg() != 0 { + if b.specFD == -1 || b.controllerFD == -1 || f.NArg() != 0 { f.Usage() return subcommands.ExitUsageError } @@ -86,8 +90,10 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) // Ensure that if there is a panic, all goroutine stacks are printed. debug.SetTraceback("all") - // Get the spec from the bundleDir. - spec, err := specutils.ReadSpec(b.bundleDir) + // Get the spec from the specFD. + specFile := os.NewFile(uintptr(b.specFD), "spec file") + defer specFile.Close() + spec, err := specutils.ReadSpecFromFile(b.bundleDir, specFile) if err != nil { Fatalf("error reading spec: %v", err) } @@ -123,6 +129,11 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) args = append(args, arg) } } + + // Note that we've already read the spec from the spec FD, and + // we will read it again after the exec call. This works + // because the ReadSpecFromFile function seeks to the beginning + // of the file before reading. if err := setCapsAndCallSelf(args, caps); err != nil { Fatalf("%v", err) } diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 94a889077..38ae03e7a 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -15,6 +15,8 @@ package cmd import ( + "path/filepath" + "context" "flag" "github.com/google/subcommands" @@ -83,6 +85,7 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} Fatalf("error reading spec: %v", err) } specutils.LogSpec(spec) + conf.SpecFile = filepath.Join(bundleDir, "config.json") // Create the container. A new sandbox will be created for the // container unless the metadata specifies that it should be run in an diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index 681112f30..92aa6bc40 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -15,6 +15,7 @@ package cmd import ( + "path/filepath" "syscall" "context" @@ -71,6 +72,8 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s if err != nil { Fatalf("error reading spec: %v", err) } + specutils.LogSpec(spec) + conf.SpecFile = filepath.Join(bundleDir, "config.json") ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile) if err != nil { diff --git a/runsc/main.go b/runsc/main.go index 773ec6486..0c9b9af78 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -17,13 +17,11 @@ package main import ( - "fmt" "io" "os" "path/filepath" "strings" "syscall" - "time" "context" "flag" @@ -32,6 +30,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/cmd" + "gvisor.googlesource.com/gvisor/runsc/specutils" ) var ( @@ -48,6 +47,8 @@ var ( // Debugging flags. debugLogDir = flag.String("debug-log-dir", "", "additional location for logs. It creates individual log files per command") logPackets = flag.Bool("log-packets", false, "enable network packet logging") + logFD = flag.Int("log-fd", -1, "file descriptor to log to. If set, the 'log' flag is ignored.") + debugLogFD = flag.Int("debug-log-fd", -1, "file descriptor to write debug logs to. If set, the 'debug-log-dir' flag is ignored.") // Debugging flags: strace related strace = flag.Bool("strace", false, "enable strace") @@ -64,6 +65,7 @@ var ( panicSignal = flag.Int("panic-signal", -1, "register signal handling that panics. Usually set to SIGUSR2(12) to troubleshoot hangs. -1 disables it.") ) +// gitRevision is set during linking. var gitRevision = "" func main() { @@ -152,7 +154,9 @@ func main() { } var logFile io.Writer = os.Stderr - if *logFilename != "" { + if *logFD > -1 { + logFile = os.NewFile(uintptr(*logFD), "log file") + } else if *logFilename != "" { // We must set O_APPEND and not O_TRUNC because Docker passes // the same log file for all commands (and also parses these // log files), so we can't destroy them on each command. @@ -173,18 +177,17 @@ func main() { cmd.Fatalf("invalid log format %q, must be 'json' or 'text'", *logFormat) } - if *debugLogDir != "" { + if *debugLogFD > -1 { + f := os.NewFile(uintptr(*debugLogFD), "debug log file") + e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} + } else if *debugLogDir != "" { if err := os.MkdirAll(*debugLogDir, 0775); err != nil { cmd.Fatalf("error creating dir %q: %v", *debugLogDir, err) } - - // Format: /runsc.log.. - scmd := flag.CommandLine.Arg(0) - filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), scmd) - path := filepath.Join(*debugLogDir, filename) - f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) + subcommand := flag.CommandLine.Arg(0) + f, err := specutils.DebugLogFile(*debugLogDir, subcommand) if err != nil { - cmd.Fatalf("error opening log file %q: %v", filename, err) + cmd.Fatalf("error opening debug log file in %q: %v", *debugLogDir, err) } e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index f14a2f8c9..e0fadefcd 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -233,34 +233,70 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 - // Create control server socket here and donate FD to child process because - // it may be in a different network namespace and won't be reachable from - // outside. - addr := boot.ControlSocketAddr(s.ID) - fd, err := server.CreateSocket(addr) - log.Infof("Creating sandbox process with addr: %s", addr[1:]) // skip "\00". - if err != nil { - return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) - } - - consoleEnabled := consoleSocket != "" - binPath, err := specutils.BinPath() if err != nil { return err } cmd := exec.Command(binPath, conf.ToFlags()...) cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.Args = append(cmd.Args, - "boot", - "--bundle", bundleDir, - "--controller-fd="+strconv.Itoa(nextFD), - "--console="+strconv.FormatBool(consoleEnabled)) - nextFD++ - controllerFile := os.NewFile(uintptr(fd), "control_server_socket") + // Open the log files to pass to the sandbox as FDs. + // + // These flags must come BEFORE the "boot" command in cmd.Args. + if conf.LogFilename != "" { + logFile, err := os.OpenFile(conf.LogFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("error opening log file %q: %v", conf.LogFilename, err) + } + defer logFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, logFile) + cmd.Args = append(cmd.Args, "--log-fd="+strconv.Itoa(nextFD)) + nextFD++ + } + if conf.DebugLogDir != "" { + debugLogFile, err := specutils.DebugLogFile(conf.DebugLogDir, "boot") + if err != nil { + return fmt.Errorf("error opening debug log file in %q: %v", conf.DebugLogDir, err) + } + defer debugLogFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, debugLogFile) + cmd.Args = append(cmd.Args, "--debug-log-fd="+strconv.Itoa(nextFD)) + nextFD++ + } + + // Add the "boot" command to the args. + // + // All flags after this must be for the boot command + cmd.Args = append(cmd.Args, "boot", "--bundle="+bundleDir) + + consoleEnabled := consoleSocket != "" + cmd.Args = append(cmd.Args, "--console="+strconv.FormatBool(consoleEnabled)) + + // Create a socket for the control server and donate it to the sandbox. + addr := boot.ControlSocketAddr(s.ID) + sockFD, err := server.CreateSocket(addr) + log.Infof("Creating sandbox process with addr: %s", addr[1:]) // skip "\00". + if err != nil { + return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err) + } + controllerFile := os.NewFile(uintptr(sockFD), "control_server_socket") defer controllerFile.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, controllerFile) + cmd.Args = append(cmd.Args, "--controller-fd="+strconv.Itoa(nextFD)) + nextFD++ + + // Open the spec file to donate to the sandbox. + if conf.SpecFile == "" { + return fmt.Errorf("conf.SpecFile must be set") + } + specFile, err := os.Open(conf.SpecFile) + if err != nil { + return fmt.Errorf("error opening spec file %q: %v", conf.SpecFile, err) + } + defer specFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, specFile) + cmd.Args = append(cmd.Args, "--spec-fd="+strconv.Itoa(nextFD)) + nextFD++ // If there is a gofer, sends all socket ends to the sandbox. for _, f := range ioFiles { @@ -357,6 +393,11 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace}) } + // Log the fds we are donating to the sandbox process. + for i, f := range cmd.ExtraFiles { + log.Debugf("Donating FD %d: %q", i+3, f.Name()) + } + log.Debugf("Starting sandbox: %s %v", binPath, cmd.Args) if err := specutils.StartInNS(cmd, nss); err != nil { return err diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 6c1ac56c3..3234cc088 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -126,14 +126,28 @@ func absPath(base, rel string) string { // path, e.g. spec.Root.Path, mount.Source. func ReadSpec(bundleDir string) (*specs.Spec, error) { // The spec file must be in "config.json" inside the bundle directory. - specFile := filepath.Join(bundleDir, "config.json") - specBytes, err := ioutil.ReadFile(specFile) + specPath := filepath.Join(bundleDir, "config.json") + specFile, err := os.Open(specPath) if err != nil { - return nil, fmt.Errorf("error reading spec from file %q: %v", specFile, err) + return nil, fmt.Errorf("error opening spec file %q: %v", specPath, err) + } + defer specFile.Close() + return ReadSpecFromFile(bundleDir, specFile) +} + +// ReadSpecFromFile reads an OCI runtime spec from the given File, and +// normalizes all relative paths into absolute by prepending the bundle dir. +func ReadSpecFromFile(bundleDir string, specFile *os.File) (*specs.Spec, error) { + if _, err := specFile.Seek(0, os.SEEK_SET); err != nil { + return nil, fmt.Errorf("error seeking to beginning of file %q: %v", specFile.Name(), err) + } + specBytes, err := ioutil.ReadAll(specFile) + if err != nil { + return nil, fmt.Errorf("error reading spec from file %q: %v", specFile.Name(), err) } var spec specs.Spec if err := json.Unmarshal(specBytes, &spec); err != nil { - return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile, err, string(specBytes)) + return nil, fmt.Errorf("error unmarshaling spec from file %q: %v\n %s", specFile.Name(), err, string(specBytes)) } if err := ValidateSpec(&spec); err != nil { return nil, err @@ -372,3 +386,11 @@ func WaitForReady(pid int, timeout time.Duration, ready func() (bool, error)) er } return backoff.Retry(op, b) } + +// DebugLogFile opens a file in logDir based on the timestamp and subcommand +// for writing. +func DebugLogFile(logDir, subcommand string) (*os.File, error) { + // Format: /runsc.log.. + filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), subcommand) + return os.OpenFile(filepath.Join(logDir, filename), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) +} diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 25987d040..4429b981b 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -176,6 +176,7 @@ func SetupContainerInRoot(rootDir string, spec *specs.Spec, conf *boot.Config) ( } conf.RootDir = rootDir + conf.SpecFile = filepath.Join(bundleDir, "config.json") return bundleDir, nil } -- cgit v1.2.3 From 0a9a40abcda602dc3403e2108e1348bf4e04051a Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Tue, 4 Sep 2018 20:31:52 -0700 Subject: runsc: Run sandbox as user nobody. When starting a sandbox without direct file or network access, we create an empty user namespace and run the sandbox in there. However, the root user in that namespace is still mapped to the root user in the parent namespace. This CL maps the "nobody" user from the parent namespace into the child namespace, and runs the sandbox process as user "nobody" inside the new namespace. PiperOrigin-RevId: 211572223 Change-Id: I1b1f9b1a86c0b4e7e5ca7bc93be7d4887678bab6 --- README.md | 7 ++++--- runsc/boot/config.go | 5 +++++ runsc/sandbox/sandbox.go | 33 +++++++++++++++++++++++++++++++-- runsc/specutils/BUILD | 1 + runsc/specutils/namespace.go | 14 ++++++++++++++ runsc/test/testutil/testutil.go | 15 ++++++++------- 6 files changed, 63 insertions(+), 12 deletions(-) (limited to 'runsc/specutils') diff --git a/README.md b/README.md index 4ec83ab0c..d85948ce5 100644 --- a/README.md +++ b/README.md @@ -179,14 +179,15 @@ here: `https://storage.googleapis.com/gvisor/releases/nightly/${yyyy-mm-dd}/runsc.sha512` **It is important to copy this binary to some place that is accessible to all -users**, since `runsc` executes itself as user `nobody` to avoid unnecessary -privileges. The `/usr/local/bin` directory is a good choice. +users, and make is executable to all users**, since `runsc` executes itself as +user `nobody` to avoid unnecessary privileges. The `/usr/local/bin` directory is +a good place to put the `runsc` binary. ``` wget https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc wget https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc.sha512 sha512sum -c runsc.sha512 -chmod +x runsc +chmod a+x runsc sudo mv runsc /usr/local/bin ``` diff --git a/runsc/boot/config.go b/runsc/boot/config.go index 212f5b003..87a47dd0b 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -213,6 +213,11 @@ type Config struct { // PanicSignal register signal handling that panics. Usually set to // SIGUSR2(12) to troubleshoot hangs. -1 disables it. PanicSignal int + + // TestOnlyAllowRunAsCurrentUser should only be used in tests. It + // allows runsc to start the sandbox process as the current user if we + // do not have capability to set uid/gid to another user. + TestOnlyAllowRunAsCurrentUser bool } // ToFlags returns a slice of flags that correspond to the given Config. diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index e0fadefcd..dd5a0aa56 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -373,8 +373,8 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // User namespace depends on the following options: // - Host network/filesystem: requires to run inside the user namespace // specified in the spec or the current namespace if none is configured. - // - Gofer: when using a Gofer, the sandbox process can run isolated in an - // empty namespace. + // - Gofer: when using a Gofer, the sandbox process can run isolated in a + // new user namespace with only the "nobody" user and group. if conf.Network == boot.NetworkHost || conf.FileAccess == boot.FileAccessDirect { if userns, ok := specutils.GetNS(specs.UserNamespace, spec); ok { log.Infof("Sandbox will be started in container's user namespace: %+v", userns) @@ -391,6 +391,34 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } else { log.Infof("Sandbox will be started in new user namespace") nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace}) + + if conf.TestOnlyAllowRunAsCurrentUser { + log.Warningf("Running sandbox in test mode as current user (uid=%d gid=%d). This is only safe in tests!", os.Getuid(), os.Getgid()) + } else if specutils.CanSetUIDGID() { + // If we have CAP_SETUID and CAP_SETGID, then we can also run + // as user nobody. + + // Map nobody in the new namespace to nobody in the parent namespace. + const nobody = 65534 + cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ + ContainerID: int(nobody), + HostID: int(nobody), + Size: int(1), + }} + cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{ + ContainerID: int(nobody), + HostID: int(nobody), + Size: int(1), + }} + + // Set credentials to run as user and group nobody. + cmd.SysProcAttr.Credential = &syscall.Credential{ + Uid: nobody, + Gid: nobody, + } + } else { + return fmt.Errorf("can't run sandbox process as user nobody since we don't have CAP_SETUID or CAP_SETGID") + } } // Log the fds we are donating to the sandbox process. @@ -399,6 +427,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } log.Debugf("Starting sandbox: %s %v", binPath, cmd.Args) + log.Debugf("SysProcAttr: %+v", cmd.SysProcAttr) if err := specutils.StartInNS(cmd, nss); err != nil { return err } diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 97a504b20..e73b2293f 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -18,6 +18,7 @@ go_library( "//pkg/sentry/kernel/auth", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go index 80eaad965..356943a65 100644 --- a/runsc/specutils/namespace.go +++ b/runsc/specutils/namespace.go @@ -23,6 +23,7 @@ import ( "syscall" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" "golang.org/x/sys/unix" "gvisor.googlesource.com/gvisor/pkg/log" ) @@ -202,3 +203,16 @@ func SetUIDGIDMappings(cmd *exec.Cmd, s *specs.Spec) { }) } } + +// CanSetUIDGID returns true if the user has SETUID and SETGID capabilities. +func CanSetUIDGID() bool { + caps, err := capability.NewPid2(os.Getpid()) + if err != nil { + return false + } + if err := caps.Load(); err != nil { + return false + } + return caps.Get(capability.EFFECTIVE, capability.CAP_SETUID) && + caps.Get(capability.EFFECTIVE, capability.CAP_SETGID) +} diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 4429b981b..77bd56912 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -104,13 +104,14 @@ func FindFile(path string) (string, error) { // TestConfig return the default configuration to use in tests. func TestConfig() *boot.Config { return &boot.Config{ - Debug: true, - LogFormat: "text", - LogPackets: true, - Network: boot.NetworkNone, - Strace: true, - MultiContainer: true, - FileAccess: boot.FileAccessProxyExclusive, + Debug: true, + LogFormat: "text", + LogPackets: true, + Network: boot.NetworkNone, + Strace: true, + MultiContainer: true, + FileAccess: boot.FileAccessProxyExclusive, + TestOnlyAllowRunAsCurrentUser: true, } } -- cgit v1.2.3 From f96b33c73c2150632a8a1ba22b1a420ec1f1214d Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Wed, 5 Sep 2018 13:00:08 -0700 Subject: runsc: Promote getExecutablePathInternal to getExecutablePath. Remove GetExecutablePath (the non-internal version). This makes path handling more consistent between exec, root, and child containers. The new getExecutablePath now uses MountNamespace.FindInode, which is more robust than Walking the Dirent tree ourselves. This also removes the last use of lstat(2) in the sentry, so that can be removed from the filters. PiperOrigin-RevId: 211683110 Change-Id: Ic8ec960fc1c267aa7d310b8efe6e900c88a9207a --- runsc/boot/controller.go | 10 ++++++ runsc/boot/filter/config.go | 5 +-- runsc/boot/fs.go | 65 ++++++++++++++++++++++++++------------- runsc/boot/loader.go | 31 +++++++++---------- runsc/cmd/exec.go | 10 ------ runsc/container/container_test.go | 3 -- runsc/specutils/specutils.go | 31 ------------------- 7 files changed, 69 insertions(+), 86 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index fdb6be5b1..ec1110059 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -228,6 +228,16 @@ func (cm *containerManager) Start(args *StartArgs, _ *struct{}) error { // Execute runs a command on a created or running sandbox. func (cm *containerManager) Execute(e *control.ExecArgs, waitStatus *uint32) error { log.Debugf("containerManager.Execute: %+v", *e) + + if e.Filename == "" { + rootCtx := cm.l.rootProcArgs.NewContext(cm.l.k) + rootMns := cm.l.k.RootMountNamespace() + var err error + if e.Filename, err = getExecutablePath(rootCtx, rootMns, e.Argv[0], e.Envv); err != nil { + return fmt.Errorf("error getting executable path for %q: %v", e.Argv[0], err) + } + } + proc := control.Proc{Kernel: cm.l.k} if err := proc.Exec(e, waitStatus); err != nil { return fmt.Errorf("error executing: %+v: %v", e, err) diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go index 113023bdd..f864b1f45 100644 --- a/runsc/boot/filter/config.go +++ b/runsc/boot/filter/config.go @@ -164,10 +164,7 @@ var allowedSyscalls = seccomp.SyscallRules{ seccomp.AllowAny{}, /* winsize struct */ }, }, - syscall.SYS_LSEEK: {}, - // TODO: Remove SYS_LSTAT when executable lookup moves - // into the gofer. - syscall.SYS_LSTAT: {}, + syscall.SYS_LSEEK: {}, syscall.SYS_MADVISE: {}, syscall.SYS_MINCORE: {}, syscall.SYS_MMAP: []seccomp.Rule{ diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 20d0e42ef..4a11b30f1 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -701,13 +701,13 @@ func setFileSystemForProcess(procArgs *kernel.CreateProcessArgs, spec *specs.Spe return nil } -// GetExecutablePathInternal traverses the *container's* filesystem to resolve -// exec's absolute path. For example, if the container is being served files by -// the fsgofer serving /foo/bar as the container root, it will search within +// getExecutablePath traverses the *container's* filesystem to resolve exec's +// absolute path. For example, if the container is being served files by the +// fsgofer serving /foo/bar as the container root, it will search within // /foo/bar, not the host root. // TODO: Unit test this. -func GetExecutablePathInternal(ctx context.Context, procArgs *kernel.CreateProcessArgs) (string, error) { - exec := filepath.Clean(procArgs.Filename) +func getExecutablePath(ctx context.Context, mns *fs.MountNamespace, filename string, env []string) (string, error) { + exec := filepath.Clean(filename) // Don't search PATH if exec is a path to a file (absolute or relative). if strings.IndexByte(exec, '/') >= 0 { @@ -716,31 +716,52 @@ func GetExecutablePathInternal(ctx context.Context, procArgs *kernel.CreateProce // Search the PATH for a file whose name matches the one we are looking // for. - pathDirs := specutils.GetPath(procArgs.Envv) + pathDirs := specutils.GetPath(env) for _, p := range pathDirs { - // Walk to the end of the path. - curDir := procArgs.Root - for _, pc := range strings.Split(p, "/") { - var err error - if curDir, err = curDir.Walk(ctx, curDir, pc); err != nil { - break - } - } - if curDir == nil { + // Try to find the binary inside path p. + binPath := path.Join(p, filename) + root := fs.RootFromContext(ctx) + defer root.DecRef() + d, err := mns.FindInode(ctx, root, nil, binPath, linux.MaxSymlinkTraversals) + if err == syserror.ENOENT || err == syserror.EACCES { continue } - // Check for the executable in the path directory. - dirent, err := curDir.Walk(ctx, curDir, exec) if err != nil { - continue + return "", fmt.Errorf("FindInode(%q) failed: %v", binPath, err) } - // Check whether we can read and execute the file in question. - if err := dirent.Inode.CheckPermission(ctx, fs.PermMask{Read: true, Execute: true}); err != nil { - log.Infof("Found executable at %q, but user cannot execute it: %v", path.Join(p, exec), err) + defer d.DecRef() + + // Check whether we can read and execute the found file. + if err := d.Inode.CheckPermission(ctx, fs.PermMask{Read: true, Execute: true}); err != nil { + log.Infof("Found executable at %q, but user cannot execute it: %v", binPath, err) continue } return path.Join("/", p, exec), nil } - return "", fmt.Errorf("could not find executable %s in path %v", exec, pathDirs) + return "", fmt.Errorf("could not find executable %q in path %v", exec, pathDirs) +} + +// setExecutablePath sets the procArgs.Filename by searching the PATH for an +// executable matching the procArgs.Argv[0]. +func setExecutablePath(ctx context.Context, mns *fs.MountNamespace, procArgs *kernel.CreateProcessArgs) error { + if procArgs.Filename != "" { + // Sanity check. + if !path.IsAbs(procArgs.Filename) { + return fmt.Errorf("filename must be absolute: %q", procArgs.Filename) + } + // Nothing to set. + return nil + } + + if len(procArgs.Argv) == 0 { + return fmt.Errorf("Argv must not be empty") + } + + f, err := getExecutablePath(ctx, mns, procArgs.Argv[0], procArgs.Envv) + if err != nil { + return err + } + procArgs.Filename = f + return nil } diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 74d0c2534..2733c4d69 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -271,16 +271,8 @@ func newProcess(spec *specs.Spec, creds *auth.Credentials, utsns *kernel.UTSName return kernel.CreateProcessArgs{}, fmt.Errorf("error creating limits: %v", err) } - // Get the executable path, which is a bit tricky because we have to - // inspect the environment PATH which is relative to the root path. - exec, err := specutils.GetExecutablePath(spec.Process.Args[0], spec.Root.Path, spec.Process.Env) - if err != nil { - return kernel.CreateProcessArgs{}, fmt.Errorf("error getting executable path: %v", err) - } - // Create the process arguments. procArgs := kernel.CreateProcessArgs{ - Filename: exec, Argv: spec.Process.Args, Envv: spec.Process.Env, WorkingDirectory: spec.Process.Cwd, // Defaults to '/' if empty. @@ -365,7 +357,7 @@ func (l *Loader) run() error { // If we are restoring, we do not want to create a process. // l.restore is set by the container manager when a restore call is made. if !l.restore { - err := setFileSystemForProcess( + if err := setFileSystemForProcess( &l.rootProcArgs, l.spec, l.conf, @@ -374,10 +366,16 @@ func (l *Loader) run() error { l.rootProcArgs.Credentials, l.rootProcArgs.Limits, l.k, - "" /* CID, which isn't needed for the root container */) - if err != nil { + "" /* CID, which isn't needed for the root container */); err != nil { return err } + + rootCtx := l.rootProcArgs.NewContext(l.k) + rootMns := l.k.RootMountNamespace() + if err := setExecutablePath(rootCtx, rootMns, &l.rootProcArgs); err != nil { + return fmt.Errorf("error setting executable path for %+v: %v", l.rootProcArgs, err) + } + // Create the root container init task. if _, err := l.k.CreateProcess(l.rootProcArgs); err != nil { return fmt.Errorf("failed to create init process: %v", err) @@ -443,7 +441,7 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config ioFDs = append(ioFDs, fd) } - err = setFileSystemForProcess( + if err := setFileSystemForProcess( &procArgs, spec, conf, @@ -452,13 +450,14 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config creds, procArgs.Limits, k, - cid) - if err != nil { + cid); err != nil { return 0, fmt.Errorf("failed to create new process: %v", err) } - if procArgs.Filename, err = GetExecutablePathInternal(procArgs.NewContext(k), &procArgs); err != nil { - return 0, err + ctx := procArgs.NewContext(l.k) + mns := k.RootMountNamespace() + if err := setExecutablePath(ctx, mns, &procArgs); err != nil { + return 0, fmt.Errorf("error setting executable path for %+v: %v", procArgs, err) } tg, err := l.k.CreateProcess(procArgs) diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index b84a80119..966d2e258 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -140,16 +140,6 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } } - // Get the executable path, which is a bit tricky because we have to - // inspect the environment PATH which is relative to the root path. - // If the user is overriding environment variables, PATH may have been - // overwritten. - rootPath := c.Spec.Root.Path - e.Filename, err = specutils.GetExecutablePath(e.Argv[0], rootPath, e.Envv) - if err != nil { - Fatalf("error getting executable path: %v", err) - } - ws, err := c.Execute(e) if err != nil { Fatalf("error getting processes for container: %v", err) diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index e7e53c492..5f452acbf 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -521,7 +521,6 @@ func TestExec(t *testing.T) { execArgs := control.ExecArgs{ Filename: "/bin/sleep", Argv: []string{"sleep", "5"}, - Envv: []string{"PATH=" + os.Getenv("PATH")}, WorkingDirectory: "/", KUID: uid, } @@ -889,7 +888,6 @@ func TestPauseResume(t *testing.T) { execArgs := control.ExecArgs{ Filename: "/bin/bash", Argv: []string{"bash", "-c", script}, - Envv: []string{"PATH=" + os.Getenv("PATH")}, WorkingDirectory: "/", KUID: uid, } @@ -1070,7 +1068,6 @@ func TestCapabilities(t *testing.T) { execArgs := control.ExecArgs{ Filename: exePath, Argv: []string{exePath}, - Envv: []string{"PATH=" + os.Getenv("PATH")}, WorkingDirectory: "/", KUID: uid, KGID: gid, diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 3234cc088..551718e9a 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -163,37 +163,6 @@ func ReadSpecFromFile(bundleDir string, specFile *os.File) (*specs.Spec, error) return &spec, nil } -// GetExecutablePath returns the absolute path to the executable, relative to -// the root. It searches the environment PATH for the first file that exists -// with the given name. -// TODO: Remove this in favor of finding executables via -// boot.GetExecutablePathInternal. -func GetExecutablePath(exec, root string, env []string) (string, error) { - exec = filepath.Clean(exec) - - // Don't search PATH if exec is a path to a file (absolute or relative). - if strings.IndexByte(exec, '/') >= 0 { - return exec, nil - } - - // Search the PATH for a file whose name matches the one we are looking - // for. - path := GetPath(env) - for _, p := range path { - abs := filepath.Join(root, p, exec) - // Do not follow symlink link because the target is in the container - // root filesystem. - if _, err := os.Lstat(abs); err == nil { - // We found it! Return the path relative to the root. - return filepath.Join("/", p, exec), nil - } - } - - // Could not find a suitable path, just return the original string. - log.Warningf("could not find executable %s in path %s", exec, path) - return exec, nil -} - // GetPath returns the PATH as a slice of strings given the environemnt // variables. func GetPath(env []string) []string { -- cgit v1.2.3 From 210c2520890ea48d551c0c9fffe890a7c60fb802 Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Fri, 7 Sep 2018 10:15:34 -0700 Subject: runsc: Run sandbox process inside minimal chroot. We construct a dir with the executable bind-mounted at /exe, and proc mounted at /proc. Runsc now executes the sandbox process inside this chroot, thus limiting access to the host filesystem. The mounts and chroot dir are removed when the sandbox is destroyed. Because this requires bind-mounts, we can only do the chroot if we have CAP_SYS_ADMIN. PiperOrigin-RevId: 211994001 Change-Id: Ia71c515e26085e0b69b833e71691830148bc70d1 --- runsc/boot/config.go | 9 +-- runsc/container/fs.go | 30 ++-------- runsc/sandbox/BUILD | 1 + runsc/sandbox/chroot.go | 120 ++++++++++++++++++++++++++++++++++++++++ runsc/sandbox/sandbox.go | 33 +++++++++-- runsc/specutils/namespace.go | 12 ++++ runsc/specutils/specutils.go | 41 ++++++++++++++ runsc/test/testutil/BUILD | 1 - runsc/test/testutil/testutil.go | 26 +++------ 9 files changed, 221 insertions(+), 52 deletions(-) create mode 100644 runsc/sandbox/chroot.go (limited to 'runsc/specutils') diff --git a/runsc/boot/config.go b/runsc/boot/config.go index 87a47dd0b..28a1600cd 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -214,10 +214,11 @@ type Config struct { // SIGUSR2(12) to troubleshoot hangs. -1 disables it. PanicSignal int - // TestOnlyAllowRunAsCurrentUser should only be used in tests. It - // allows runsc to start the sandbox process as the current user if we - // do not have capability to set uid/gid to another user. - TestOnlyAllowRunAsCurrentUser bool + // TestOnlyAllowRunAsCurrentUserWithoutChroot should only be used in + // tests. It allows runsc to start the sandbox process as the current + // user, and without chrooting the sandbox process. This can be + // necessary in test environments that have limited capabilities. + TestOnlyAllowRunAsCurrentUserWithoutChroot bool } // ToFlags returns a slice of flags that correspond to the given Config. diff --git a/runsc/container/fs.go b/runsc/container/fs.go index fb352fc7c..a3c5772ba 100644 --- a/runsc/container/fs.go +++ b/runsc/container/fs.go @@ -77,11 +77,6 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { if m.Type != "bind" || !specutils.IsSupportedDevMount(m) { continue } - src := m.Source - srcfi, err := os.Stat(src) - if err != nil { - return fmt.Errorf("failed to stat() mount source: %v", err) - } // It's possible that 'm.Destination' follows symlinks inside the // container. @@ -90,30 +85,13 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { return fmt.Errorf("failed to resolve symlinks: %v", err) } - // Create mount point if it doesn't exits - if _, err := os.Stat(dst); os.IsNotExist(err) { - if srcfi.IsDir() { - if err := os.MkdirAll(dst, 0755); err != nil { - return fmt.Errorf("failed to make mount directory %q: %v", dst, err) - } - } else { - if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { - return fmt.Errorf("failed to make mount directory for file %q: %v", filepath.Dir(dst), err) - } - f, err := os.OpenFile(dst, os.O_CREATE, 0755) - if err != nil { - return fmt.Errorf("failed to open mount file %q: %v", dst, err) - } - f.Close() - } - } - flags := optionsToFlags(m.Options) flags |= syscall.MS_BIND - log.Infof("Mounting src: %q, dst: %q, flags: %#x", src, dst, flags) - if err := syscall.Mount(src, dst, m.Type, uintptr(flags), ""); err != nil { - return fmt.Errorf("failed to mount src: %q, dst: %q, flags: %#x, err: %v", src, dst, flags, err) + log.Infof("Mounting src: %q, dst: %q, flags: %#x", m.Source, dst, flags) + if err := specutils.Mount(m.Source, dst, m.Type, flags); err != nil { + return fmt.Errorf("failed to mount %v: %v", m, err) } + // Make the mount a slave, so that for recursive bind mount, umount won't // propagate to the source. flags = syscall.MS_SLAVE | syscall.MS_REC diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index 9317b1c14..8ebd14c4e 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -5,6 +5,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "sandbox", srcs = [ + "chroot.go", "network.go", "sandbox.go", ], diff --git a/runsc/sandbox/chroot.go b/runsc/sandbox/chroot.go new file mode 100644 index 000000000..a77a186c2 --- /dev/null +++ b/runsc/sandbox/chroot.go @@ -0,0 +1,120 @@ +// Copyright 2018 Google Inc. +// +// 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 sandbox + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/specutils" +) + +// chrootBinPath is the location inside the chroot where the runsc binary will +// be mounted. +const chrootBinPath = "/runsc" + +// mountInChroot creates the destination mount point in the given chroot and +// mounts the source. +func mountInChroot(chroot, src, dst, typ string, flags uint32) error { + chrootDst := filepath.Join(chroot, dst) + log.Infof("Mounting %q at %q", src, chrootDst) + + return specutils.Mount(src, chrootDst, typ, flags) +} + +// setUpChroot creates an empty directory with runsc mounted at /runsc, proc +// mounted at /proc, and any dev files needed for the platform. +func setUpChroot(platform boot.PlatformType) (string, error) { + // Create the chroot directory and make it accessible to all users. + chroot, err := ioutil.TempDir("", "runsc-sandbox-chroot-") + if err != nil { + return "", fmt.Errorf("TempDir() failed: %v", err) + } + if err := os.Chmod(chroot, 0777); err != nil { + return "", fmt.Errorf("Chmod(%q) failed: %v", chroot, err) + } + log.Infof("Setting up sandbox chroot in %q", chroot) + + // Mount /proc. + if err := mountInChroot(chroot, "proc", "/proc", "proc", 0); err != nil { + return "", fmt.Errorf("error mounting proc in chroot: %v", err) + } + + // Mount runsc at /runsc in the chroot. + binPath, err := specutils.BinPath() + if err != nil { + return "", err + } + if err := mountInChroot(chroot, binPath, chrootBinPath, "bind", syscall.MS_BIND|syscall.MS_RDONLY); err != nil { + return "", fmt.Errorf("error mounting runsc in chroot: %v", err) + } + + // Mount dev files needed for platform. + var devMount string + switch platform { + case boot.PlatformKVM: + devMount = "/dev/kvm" + } + if devMount != "" { + if err := mountInChroot(chroot, devMount, devMount, "bind", syscall.MS_BIND); err != nil { + return "", fmt.Errorf("error mounting platform device in chroot: %v", err) + } + } + + return chroot, nil +} + +// tearDownChroot unmounts /proc and /runsc from the chroot before deleting the +// directory. +func tearDownChroot(chroot string) error { + // Unmount /proc. + proc := filepath.Join(chroot, "proc") + if err := syscall.Unmount(proc, 0); err != nil { + return fmt.Errorf("error unmounting %q: %v", proc, err) + } + + // Unmount /runsc. + exe := filepath.Join(chroot, chrootBinPath) + if err := syscall.Unmount(exe, 0); err != nil { + return fmt.Errorf("error unmounting %q: %v", exe, err) + } + + // Unmount platform dev files. + devFiles := []string{"dev/kvm"} + for _, f := range devFiles { + devPath := filepath.Join(chroot, f) + if _, err := os.Stat(devPath); err != nil { + if os.IsNotExist(err) { + continue + } + return fmt.Errorf("Stat(%q) failed: %v", devPath, err) + } + if err := syscall.Unmount(devPath, 0); err != nil { + return fmt.Errorf("error unmounting %q: %v", devPath, err) + } + } + + // Remove chroot directory. + if err := os.RemoveAll(chroot); err != nil { + return fmt.Errorf("error removing %q: %v", chroot, err) + } + + return nil +} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index dd5a0aa56..f6264d5b2 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -51,6 +51,10 @@ type Sandbox struct { // Pid is the pid of the running sandbox (immutable). May be 0 is the sandbox // is not running. Pid int `json:"pid"` + + // Chroot is the path to the chroot directory that the sandbox process + // is running in. + Chroot string `json:"chroot"` } // Create creates the sandbox process. @@ -392,12 +396,11 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund log.Infof("Sandbox will be started in new user namespace") nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace}) - if conf.TestOnlyAllowRunAsCurrentUser { + // If we have CAP_SETUID and CAP_SETGID, then we can also run + // as user nobody. + if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { log.Warningf("Running sandbox in test mode as current user (uid=%d gid=%d). This is only safe in tests!", os.Getuid(), os.Getgid()) } else if specutils.CanSetUIDGID() { - // If we have CAP_SETUID and CAP_SETGID, then we can also run - // as user nobody. - // Map nobody in the new namespace to nobody in the parent namespace. const nobody = 65534 cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ @@ -419,6 +422,23 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } else { return fmt.Errorf("can't run sandbox process as user nobody since we don't have CAP_SETUID or CAP_SETGID") } + + // If we have CAP_SYS_ADMIN, we can create an empty chroot and + // bind-mount the executable inside it. + if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { + log.Warningf("Running sandbox in test mode without chroot. This is only safe in tests!") + } else if specutils.HasCapSysAdmin() { + log.Infof("Sandbox will be started in minimal chroot") + chroot, err := setUpChroot(conf.Platform) + if err != nil { + return fmt.Errorf("error setting up chroot: %v", err) + } + cmd.SysProcAttr.Chroot = chroot + cmd.Args[0] = "/runsc" + cmd.Path = "/runsc" + } else { + return fmt.Errorf("can't run sandbox process in minimal chroot since we don't have CAP_SYS_ADMIN") + } } // Log the fds we are donating to the sandbox process. @@ -525,6 +545,11 @@ func (s *Sandbox) Destroy() error { log.Debugf("Killing sandbox %q", s.ID) signalProcess(s.Pid, unix.SIGKILL) } + + if s.Chroot != "" { + return tearDownChroot(s.Chroot) + } + return nil } diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go index 356943a65..48a199a77 100644 --- a/runsc/specutils/namespace.go +++ b/runsc/specutils/namespace.go @@ -216,3 +216,15 @@ func CanSetUIDGID() bool { return caps.Get(capability.EFFECTIVE, capability.CAP_SETUID) && caps.Get(capability.EFFECTIVE, capability.CAP_SETGID) } + +// HasCapSysAdmin returns true if the user has CAP_SYS_ADMIN capability. +func HasCapSysAdmin() bool { + caps, err := capability.NewPid2(os.Getpid()) + if err != nil { + return false + } + if err := caps.Load(); err != nil { + return false + } + return caps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) +} diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 551718e9a..f3fa8d129 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -363,3 +363,44 @@ func DebugLogFile(logDir, subcommand string) (*os.File, error) { filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), subcommand) return os.OpenFile(filepath.Join(logDir, filename), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) } + +// Mount creates the mount point and calls Mount with the given flags. +func Mount(src, dst, typ string, flags uint32) error { + // Create the mount point inside. The type must be the same as the + // source (file or directory). + var isDir bool + if typ == "proc" { + // Special case, as there is no source directory for proc + // mounts. + isDir = true + } else if fi, err := os.Stat(src); err != nil { + return fmt.Errorf("Stat(%q) failed: %v", src, err) + } else { + isDir = fi.IsDir() + } + + if isDir { + // Create the destination directory. + if err := os.MkdirAll(dst, 0777); err != nil { + return fmt.Errorf("Mkdir(%q) failed: %v", dst, err) + } + } else { + // Create the parent destination directory. + parent := path.Dir(dst) + if err := os.MkdirAll(parent, 0777); err != nil { + return fmt.Errorf("Mkdir(%q) failed: %v", parent, err) + } + // Create the destination file if it does not exist. + f, err := os.OpenFile(dst, syscall.O_CREAT, 0777) + if err != nil { + return fmt.Errorf("Open(%q) failed: %v", dst, err) + } + f.Close() + } + + // Do the mount. + if err := syscall.Mount(src, dst, typ, uintptr(flags), ""); err != nil { + return fmt.Errorf("Mount(%q, %q, %d) failed: %v", src, dst, flags, err) + } + return nil +} diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index ca91e07ff..03ab3c4ac 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -18,6 +18,5 @@ go_library( "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", - "@com_github_syndtr_gocapability//capability:go_default_library", ], ) diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 77bd56912..4f012a8ea 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -32,7 +32,6 @@ import ( "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/syndtr/gocapability/capability" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -104,14 +103,14 @@ func FindFile(path string) (string, error) { // TestConfig return the default configuration to use in tests. func TestConfig() *boot.Config { return &boot.Config{ - Debug: true, - LogFormat: "text", - LogPackets: true, - Network: boot.NetworkNone, - Strace: true, - MultiContainer: true, - FileAccess: boot.FileAccessProxyExclusive, - TestOnlyAllowRunAsCurrentUser: true, + Debug: true, + LogFormat: "text", + LogPackets: true, + Network: boot.NetworkNone, + Strace: true, + MultiContainer: true, + FileAccess: boot.FileAccessProxyExclusive, + TestOnlyAllowRunAsCurrentUserWithoutChroot: true, } } @@ -238,14 +237,7 @@ func WaitForHTTP(port int, timeout time.Duration) error { // RunAsRoot ensures the test runs with CAP_SYS_ADMIN. If need it will create // a new user namespace and reexecute the test as root inside of the namespace. func RunAsRoot(m *testing.M) { - caps, err := capability.NewPid2(os.Getpid()) - if err != nil { - panic(err.Error()) - } - if err := caps.Load(); err != nil { - panic(err.Error()) - } - if caps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) { + if specutils.HasCapSysAdmin() { // Capability: check! Good to run. os.Exit(m.Run()) } -- cgit v1.2.3 From 9751b800a6835f7febf99f1dee22a5aedd43f381 Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Fri, 7 Sep 2018 17:38:34 -0700 Subject: runsc: Support multi-container exec. We must use a context.Context with a Root Dirent that corresponds to the container's chroot. Previously we were using the root context, which does not have a chroot. Getting the correct context required refactoring some of the path-lookup code. We can't lookup the path without a context.Context, which requires kernel.CreateProcArgs, which we only get inside control.Execute. So we have to do the path lookup much later than we previously were. PiperOrigin-RevId: 212064734 Change-Id: I84a5cfadacb21fd9c3ab9c393f7e308a40b9b537 --- pkg/sentry/control/proc.go | 18 +++++++++++- pkg/sentry/fs/mounts.go | 66 ++++++++++++++++++++++++++++++++++++++++++++ runsc/boot/controller.go | 36 ++++++++++++++++++------ runsc/boot/fs.go | 58 ++------------------------------------ runsc/sandbox/sandbox.go | 7 ++++- runsc/specutils/specutils.go | 12 -------- 6 files changed, 118 insertions(+), 79 deletions(-) (limited to 'runsc/specutils') diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go index 6949a3ae5..289b8ba0e 100644 --- a/pkg/sentry/control/proc.go +++ b/pkg/sentry/control/proc.go @@ -54,6 +54,11 @@ type ExecArgs struct { // Envv is a list of environment variables. Envv []string `json:"envv"` + // Root defines the root directory for the new process. A reference on + // Root must be held for the lifetime of the ExecArgs. If Root is nil, + // it will default to the VFS root. + Root *fs.Dirent + // WorkingDirectory defines the working directory for the new process. WorkingDirectory string `json:"wd"` @@ -99,6 +104,7 @@ func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error { Argv: args.Argv, Envv: args.Envv, WorkingDirectory: args.WorkingDirectory, + Root: args.Root, Credentials: creds, FDMap: fdm, Umask: 0022, @@ -109,8 +115,18 @@ func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error { AbstractSocketNamespace: proc.Kernel.RootAbstractSocketNamespace(), } ctx := initArgs.NewContext(proc.Kernel) - mounter := fs.FileOwnerFromContext(ctx) + if initArgs.Filename == "" { + // Get the full path to the filename from the PATH env variable. + paths := fs.GetPath(initArgs.Envv) + f, err := proc.Kernel.RootMountNamespace().ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths) + if err != nil { + return fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err) + } + initArgs.Filename = f + } + + mounter := fs.FileOwnerFromContext(ctx) for appFD, f := range args.FilePayload.Files { enableIoctl := args.StdioIsPty && appFD <= 2 diff --git a/pkg/sentry/fs/mounts.go b/pkg/sentry/fs/mounts.go index 0318f135d..c0a803b2d 100644 --- a/pkg/sentry/fs/mounts.go +++ b/pkg/sentry/fs/mounts.go @@ -16,9 +16,13 @@ package fs import ( "fmt" + "path" + "strings" "sync" "syscall" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/pkg/refs" "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" @@ -509,3 +513,65 @@ func (mns *MountNamespace) SyncAll(ctx context.Context) { defer mns.mu.Unlock() mns.root.SyncAll(ctx) } + +// ResolveExecutablePath resolves the given executable name given a set of +// paths that might contain it. +func (mns *MountNamespace) ResolveExecutablePath(ctx context.Context, wd, name string, paths []string) (string, error) { + // Absolute paths can be used directly. + if path.IsAbs(name) { + return name, nil + } + + // Paths with '/' in them should be joined to the working directory, or + // to the root if working directory is not set. + if strings.IndexByte(name, '/') > 0 { + if wd == "" { + wd = "/" + } + if !path.IsAbs(wd) { + return "", fmt.Errorf("working directory %q must be absolute", wd) + } + return path.Join(wd, name), nil + } + + // Otherwise, We must lookup the name in the paths, starting from the + // calling context's root directory. + root := RootFromContext(ctx) + if root == nil { + // Caller has no root. Don't bother traversing anything. + return "", syserror.ENOENT + } + defer root.DecRef() + for _, p := range paths { + binPath := path.Join(p, name) + d, err := mns.FindInode(ctx, root, nil, binPath, linux.MaxSymlinkTraversals) + if err == syserror.ENOENT || err == syserror.EACCES { + // Didn't find it here. + continue + } + if err != nil { + return "", err + } + defer d.DecRef() + + // Check whether we can read and execute the found file. + if err := d.Inode.CheckPermission(ctx, PermMask{Read: true, Execute: true}); err != nil { + log.Infof("Found executable at %q, but user cannot execute it: %v", binPath, err) + continue + } + return path.Join("/", p, name), nil + } + return "", syserror.ENOENT +} + +// GetPath returns the PATH as a slice of strings given the environemnt +// variables. +func GetPath(env []string) []string { + const prefix = "PATH=" + for _, e := range env { + if strings.HasPrefix(e, prefix) { + return strings.Split(strings.TrimPrefix(e, prefix), ":") + } + } + return nil +} diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 45aa255c4..fd5b7cc9e 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -224,21 +224,39 @@ func (cm *containerManager) Start(args *StartArgs, _ *struct{}) error { return nil } +// ExecArgs contains arguments to Execute. +type ExecArgs struct { + control.ExecArgs + + // CID is the ID of the container to exec in. + CID string +} + // Execute runs a command on a created or running sandbox. -func (cm *containerManager) Execute(e *control.ExecArgs, waitStatus *uint32) error { +func (cm *containerManager) Execute(e *ExecArgs, waitStatus *uint32) error { log.Debugf("containerManager.Execute: %+v", *e) - if e.Filename == "" { - rootCtx := cm.l.rootProcArgs.NewContext(cm.l.k) - rootMns := cm.l.k.RootMountNamespace() - var err error - if e.Filename, err = getExecutablePath(rootCtx, rootMns, e.Argv[0], e.Envv); err != nil { - return fmt.Errorf("error getting executable path for %q: %v", e.Argv[0], err) - } + // Get the container Root Dirent from the Task, since we must run this + // process with the same Root. + cm.l.mu.Lock() + tgid, ok := cm.l.containerRootTGIDs[e.CID] + cm.l.mu.Unlock() + if !ok { + return fmt.Errorf("cannot exec in container %q: no such container", e.CID) + } + t := cm.l.k.TaskSet().Root.TaskWithID(kernel.ThreadID(tgid)) + if t == nil { + return fmt.Errorf("cannot exec in container %q: no thread group with ID %d", e.CID, tgid) + } + t.WithMuLocked(func(t *kernel.Task) { + e.Root = t.FSContext().RootDirectory() + }) + if e.Root != nil { + defer e.Root.DecRef() } proc := control.Proc{Kernel: cm.l.k} - if err := proc.Exec(e, waitStatus); err != nil { + if err := proc.Exec(&e.ExecArgs, waitStatus); err != nil { return fmt.Errorf("error executing: %+v: %v", e, err) } return nil diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 3df276170..5ec9a7d03 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -16,7 +16,6 @@ package boot import ( "fmt" - "path" "path/filepath" "strconv" "strings" @@ -683,64 +682,11 @@ func setFileSystemForProcess(procArgs *kernel.CreateProcessArgs, spec *specs.Spe return nil } -// getExecutablePath traverses the *container's* filesystem to resolve exec's -// absolute path. For example, if the container is being served files by the -// fsgofer serving /foo/bar as the container root, it will search within -// /foo/bar, not the host root. -// TODO: Unit test this. -func getExecutablePath(ctx context.Context, mns *fs.MountNamespace, filename string, env []string) (string, error) { - exec := filepath.Clean(filename) - - // Don't search PATH if exec is a path to a file (absolute or relative). - if strings.IndexByte(exec, '/') >= 0 { - return exec, nil - } - - // Search the PATH for a file whose name matches the one we are looking - // for. - pathDirs := specutils.GetPath(env) - for _, p := range pathDirs { - // Try to find the binary inside path p. - binPath := path.Join(p, filename) - root := fs.RootFromContext(ctx) - defer root.DecRef() - d, err := mns.FindInode(ctx, root, nil, binPath, linux.MaxSymlinkTraversals) - if err == syserror.ENOENT || err == syserror.EACCES { - continue - } - if err != nil { - return "", fmt.Errorf("FindInode(%q) failed: %v", binPath, err) - } - defer d.DecRef() - - // Check whether we can read and execute the found file. - if err := d.Inode.CheckPermission(ctx, fs.PermMask{Read: true, Execute: true}); err != nil { - log.Infof("Found executable at %q, but user cannot execute it: %v", binPath, err) - continue - } - return path.Join("/", p, exec), nil - } - - return "", fmt.Errorf("could not find executable %q in path %v", exec, pathDirs) -} - // setExecutablePath sets the procArgs.Filename by searching the PATH for an // executable matching the procArgs.Argv[0]. func setExecutablePath(ctx context.Context, mns *fs.MountNamespace, procArgs *kernel.CreateProcessArgs) error { - if procArgs.Filename != "" { - // Sanity check. - if !path.IsAbs(procArgs.Filename) { - return fmt.Errorf("filename must be absolute: %q", procArgs.Filename) - } - // Nothing to set. - return nil - } - - if len(procArgs.Argv) == 0 { - return fmt.Errorf("Argv must not be empty") - } - - f, err := getExecutablePath(ctx, mns, procArgs.Argv[0], procArgs.Envv) + paths := fs.GetPath(procArgs.Envv) + f, err := mns.ResolveExecutablePath(ctx, procArgs.WorkingDirectory, procArgs.Argv[0], paths) if err != nil { return err } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 697210669..f272496a1 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -187,11 +187,16 @@ func (s *Sandbox) Execute(cid string, e *control.ExecArgs) (syscall.WaitStatus, } defer conn.Close() + ea := &boot.ExecArgs{ + ExecArgs: *e, + CID: cid, + } + // Send a message to the sandbox control server to start the container. var waitStatus uint32 // TODO: Pass in the container id (cid) here. The sandbox // should execute in the context of that container. - if err := conn.Call(boot.ContainerExecute, e, &waitStatus); err != nil { + if err := conn.Call(boot.ContainerExecute, ea, &waitStatus); err != nil { return 0, fmt.Errorf("error executing in sandbox: %v", err) } diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index f3fa8d129..fdc9007e0 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -163,18 +163,6 @@ func ReadSpecFromFile(bundleDir string, specFile *os.File) (*specs.Spec, error) return &spec, nil } -// GetPath returns the PATH as a slice of strings given the environemnt -// variables. -func GetPath(env []string) []string { - const prefix = "PATH=" - for _, e := range env { - if strings.HasPrefix(e, prefix) { - return strings.Split(strings.TrimPrefix(e, prefix), ":") - } - } - return nil -} - // Capabilities takes in spec and returns a TaskCapabilities corresponding to // the spec. func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) { -- cgit v1.2.3 From f0a92b6b67382a1f8da5ef2622c59afdb1c40f13 Mon Sep 17 00:00:00 2001 From: Lingfu Date: Wed, 19 Sep 2018 13:34:28 -0700 Subject: Add docker command line args support for --cpuset-cpus and --cpus `docker run --cpuset-cpus=/--cpus=` will generate cpu resource info in config.json (runtime spec file). When nginx worker_connections is configured as auto, the worker is generated according to the number of CPUs. If the cgroup is already set on the host, but it is not displayed correctly in the sandbox, performance may be degraded. This patch can get cpus info from spec file and apply to sentry on bootup, so the /proc/cpuinfo can show the correct cpu numbers. `lscpu` and other commands rely on `/sys/devices/system/cpu/online` are also affected by this patch. e.g. --cpuset-cpus=2,3 -> cpu number:2 --cpuset-cpus=4-7 -> cpu number:4 --cpus=2.8 -> cpu number:3 --cpus=0.5 -> cpu number:1 Change-Id: Ideb22e125758d4322a12be7c51795f8018e3d316 PiperOrigin-RevId: 213685199 --- runsc/boot/loader.go | 18 ++++++---- runsc/specutils/BUILD | 1 + runsc/specutils/cpu.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 runsc/specutils/cpu.go (limited to 'runsc/specutils') diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 623d04171..f906c9f95 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -20,7 +20,6 @@ import ( "math/rand" "os" "os/signal" - "runtime" "sync" "sync/atomic" "syscall" @@ -201,15 +200,20 @@ func New(spec *specs.Spec, conf *Config, controllerFD, deviceFD int, ioFDs []int caps, auth.NewRootUserNamespace()) + // Get CPU numbers from spec. + cpuNum, err := specutils.CalculateCPUNumber(spec) + if err != nil { + return nil, fmt.Errorf("cannot get cpus from spec: %v", err) + } + // Initiate the Kernel object, which is required by the Context passed // to createVFS in order to mount (among other things) procfs. if err = k.Init(kernel.InitKernelArgs{ - FeatureSet: cpuid.HostFeatureSet(), - Timekeeper: tk, - RootUserNamespace: creds.UserNamespace, - NetworkStack: networkStack, - // TODO: use number of logical processors from cgroups. - ApplicationCores: uint(runtime.NumCPU()), + FeatureSet: cpuid.HostFeatureSet(), + Timekeeper: tk, + RootUserNamespace: creds.UserNamespace, + NetworkStack: networkStack, + ApplicationCores: uint(cpuNum), Vdso: vdso, RootUTSNamespace: kernel.NewUTSNamespace(spec.Hostname, "", creds.UserNamespace), RootIPCNamespace: kernel.NewIPCNamespace(creds.UserNamespace), diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index e73b2293f..f1a99ce48 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -5,6 +5,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "specutils", srcs = [ + "cpu.go", "namespace.go", "specutils.go", ], diff --git a/runsc/specutils/cpu.go b/runsc/specutils/cpu.go new file mode 100644 index 000000000..9abe26b64 --- /dev/null +++ b/runsc/specutils/cpu.go @@ -0,0 +1,90 @@ +// Copyright 2018 Google Inc. +// +// 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 specutils + +import ( + "fmt" + "runtime" + "strconv" + "strings" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// CalculateCPUNumber calculates the number of CPUs that should be exposed +// inside the sandbox. +func CalculateCPUNumber(spec *specs.Spec) (int, error) { + // If spec does not contain CPU field, then return the number of host CPUs. + if spec == nil || spec.Linux == nil || spec.Linux.Resources == nil || spec.Linux.Resources.CPU == nil { + return runtime.NumCPU(), nil + } + cpuSpec := spec.Linux.Resources.CPU + + // If cpuSpec.Cpus is specified, then parse and return that. They must be in + // the list format for cpusets, which is "a comma-separated list of CPU + // numbers and ranges of numbers, in ASCII decimal." --man 7 cpuset. + cpus := cpuSpec.Cpus + if cpus != "" { + cpuNum := 0 + for _, subs := range strings.Split(cpus, ",") { + result, err := parseCPUNumber(subs) + if err != nil { + return 0, err + } + cpuNum += result + } + return cpuNum, nil + } + + // If CPU.Quota and CPU.Period are specified, we can divide them to get an + // approximation of the number of CPUs needed. + if cpuSpec.Quota != nil && cpuSpec.Period != nil && *cpuSpec.Period != 0 { + cpuQuota := *cpuSpec.Quota + cpuPeriod := *cpuSpec.Period + return int(cpuQuota)/int(cpuPeriod) + 1, nil + } + + // Default to number of host cpus. + return runtime.NumCPU(), nil +} + +// parseCPUNumber converts a cpuset string into the number of cpus included in +// the string , e.g. "3-6" -> 4. +func parseCPUNumber(cpus string) (int, error) { + switch cpusSlice := strings.Split(cpus, "-"); len(cpusSlice) { + case 1: + // cpus is not a range. We must only check that it is a valid number. + if _, err := strconv.Atoi(cpus); err != nil { + return 0, fmt.Errorf("invalid individual cpu number %q", cpus) + } + return 1, nil + case 2: + // cpus is a range. We must check that start and end are valid numbers, + // and calculate their difference (inclusively). + first, err := strconv.Atoi(cpusSlice[0]) + if err != nil || first < 0 { + return 0, fmt.Errorf("invalid first cpu number %q in range %q", cpusSlice[0], cpus) + } + last, err := strconv.Atoi(cpusSlice[1]) + if err != nil || last < 0 { + return 0, fmt.Errorf("invalid last cpu number %q in range %q", cpusSlice[1], cpus) + } + cpuNum := last - first + 1 + if cpuNum <= 0 { + return 0, fmt.Errorf("cpu range %q does not include positive number of cpus", cpus) + } + } + return 0, fmt.Errorf("invalid cpu string %q", cpus) +} -- cgit v1.2.3 From e3952733011df912ecaa48974832a054a45c345a Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Wed, 19 Sep 2018 17:14:20 -0700 Subject: Fix sandbox and gofer capabilities Capabilities.Set() adds capabilities, but doesn't remove existing ones that might have been loaded. Fixed the code and added tests. PiperOrigin-RevId: 213726369 Change-Id: Id7fa6fce53abf26c29b13b9157bb4c6616986fba --- runsc/boot/fs.go | 13 +---- runsc/cmd/BUILD | 9 ++++ runsc/cmd/capability.go | 63 ++++++++++++---------- runsc/cmd/capability_test.go | 121 +++++++++++++++++++++++++++++++++++++++++++ runsc/cmd/gofer.go | 33 ++++++------ runsc/specutils/specutils.go | 10 ++++ 6 files changed, 197 insertions(+), 52 deletions(-) create mode 100644 runsc/cmd/capability_test.go (limited to 'runsc/specutils') diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 420e57022..59ae5faae 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -428,13 +428,13 @@ func parseAndFilterOptions(opts []string, allowedKeys ...string) ([]string, erro kv := strings.Split(o, "=") switch len(kv) { case 1: - if contains(allowedKeys, o) { + if specutils.ContainsStr(allowedKeys, o) { out = append(out, o) continue } log.Warningf("ignoring unsupported key %q", kv) case 2: - if contains(allowedKeys, kv[0]) { + if specutils.ContainsStr(allowedKeys, kv[0]) { out = append(out, o) continue } @@ -540,15 +540,6 @@ func mountFlags(opts []string) fs.MountSourceFlags { return mf } -func contains(strs []string, str string) bool { - for _, s := range strs { - if s == str { - return true - } - } - return false -} - func mustFindFilesystem(name string) fs.Filesystem { fs, ok := fs.FindFilesystem(name) if !ok { diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index f9c091ba2..7c90ff2c5 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -55,18 +55,27 @@ go_test( name = "cmd_test", size = "small", srcs = [ + "capability_test.go", "delete_test.go", "exec_test.go", ], + data = [ + "//runsc", + ], embed = [":cmd"], deps = [ "//pkg/abi/linux", + "//pkg/log", "//pkg/sentry/control", "//pkg/sentry/kernel/auth", "//pkg/urpc", "//runsc/boot", + "//runsc/container", + "//runsc/specutils", + "//runsc/test/testutil", "@com_github_google_go-cmp//cmp:go_default_library", "@com_github_google_go-cmp//cmp/cmpopts:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", ], ) diff --git a/runsc/cmd/capability.go b/runsc/cmd/capability.go index e2410d4ad..affbb7ce3 100644 --- a/runsc/cmd/capability.go +++ b/runsc/cmd/capability.go @@ -16,56 +16,67 @@ package cmd import ( "fmt" - "os" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/syndtr/gocapability/capability" "gvisor.googlesource.com/gvisor/pkg/log" ) +var allCapTypes = []capability.CapType{ + capability.BOUNDS, + capability.EFFECTIVE, + capability.PERMITTED, + capability.INHERITABLE, + capability.AMBIENT, +} + // applyCaps applies the capabilities in the spec to the current thread. // // Note that it must be called with current thread locked. func applyCaps(caps *specs.LinuxCapabilities) error { - setter, err := capability.NewPid2(os.Getpid()) + // Load current capabilities to trim the ones not permitted. + curCaps, err := capability.NewPid2(0) if err != nil { return err } - if err := setter.Load(); err != nil { + if err := curCaps.Load(); err != nil { return err } - bounding, err := trimCaps(caps.Bounding, setter) + // Create an empty capability set to populate. + newCaps, err := capability.NewPid2(0) if err != nil { return err } - setter.Set(capability.BOUNDS, bounding...) - effective, err := trimCaps(caps.Effective, setter) - if err != nil { - return err - } - setter.Set(capability.EFFECTIVE, effective...) - - permitted, err := trimCaps(caps.Permitted, setter) - if err != nil { - return err + for _, c := range allCapTypes { + if !newCaps.Empty(c) { + panic("unloaded capabilities must be empty") + } + set, err := trimCaps(getCaps(c, caps), curCaps) + if err != nil { + return err + } + newCaps.Set(c, set...) } - setter.Set(capability.PERMITTED, permitted...) - inheritable, err := trimCaps(caps.Inheritable, setter) - if err != nil { - return err - } - setter.Set(capability.INHERITABLE, inheritable...) + return newCaps.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS) +} - ambient, err := trimCaps(caps.Ambient, setter) - if err != nil { - return err +func getCaps(which capability.CapType, caps *specs.LinuxCapabilities) []string { + switch which { + case capability.BOUNDS: + return caps.Bounding + case capability.EFFECTIVE: + return caps.Effective + case capability.PERMITTED: + return caps.Permitted + case capability.INHERITABLE: + return caps.Inheritable + case capability.AMBIENT: + return caps.Ambient } - setter.Set(capability.AMBIENT, ambient...) - - return setter.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS) + panic(fmt.Sprint("invalid capability type:", which)) } func trimCaps(names []string, setter capability.Capabilities) ([]capability.Cap, error) { diff --git a/runsc/cmd/capability_test.go b/runsc/cmd/capability_test.go new file mode 100644 index 000000000..be9ef2e7b --- /dev/null +++ b/runsc/cmd/capability_test.go @@ -0,0 +1,121 @@ +// Copyright 2018 Google Inc. +// +// 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" + "os" + "testing" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/container" + "gvisor.googlesource.com/gvisor/runsc/specutils" + "gvisor.googlesource.com/gvisor/runsc/test/testutil" +) + +func init() { + log.SetLevel(log.Debug) + if err := testutil.ConfigureExePath(); err != nil { + panic(err.Error()) + } +} + +func checkProcessCaps(pid int, wantCaps *specs.LinuxCapabilities) error { + curCaps, err := capability.NewPid2(pid) + if err != nil { + return fmt.Errorf("capability.NewPid2(%d) failed: %v", pid, err) + } + if err := curCaps.Load(); err != nil { + return fmt.Errorf("unable to load capabilities: %v", err) + } + fmt.Printf("Capabilities (PID: %d): %v\n", pid, curCaps) + + for _, c := range allCapTypes { + if err := checkCaps(c, curCaps, wantCaps); err != nil { + return err + } + } + return nil +} + +func checkCaps(which capability.CapType, curCaps capability.Capabilities, wantCaps *specs.LinuxCapabilities) error { + wantNames := getCaps(which, wantCaps) + for name, c := range capFromName { + want := specutils.ContainsStr(wantNames, name) + got := curCaps.Get(which, c) + if want != got { + if want { + return fmt.Errorf("capability %v:%s should be set", which, name) + } + return fmt.Errorf("capability %v:%s should NOT be set", which, name) + } + } + return nil +} + +func TestCapabilities(t *testing.T) { + stop := testutil.StartReaper() + defer stop() + + spec := testutil.NewSpecWithArgs("/bin/sleep", "10000") + caps := []string{ + "CAP_CHOWN", + "CAP_SYS_PTRACE", // ptrace is added due to the platform choice. + } + spec.Process.Capabilities = &specs.LinuxCapabilities{ + Permitted: caps, + Bounding: caps, + Effective: caps, + Inheritable: caps, + } + + conf := testutil.TestConfig() + + // Use --network=host to make sandbox use spec's capabilities. + conf.Network = boot.NetworkHost + + rootDir, bundleDir, err := testutil.SetupContainer(spec, conf) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + // Create and start the container. + c, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer c.Destroy() + if err := c.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + + // Check that sandbox and gofer have the proper capabilities. + if err := checkProcessCaps(c.Sandbox.Pid, spec.Process.Capabilities); err != nil { + t.Error(err) + } + if err := checkProcessCaps(c.GoferPid, goferCaps); err != nil { + t.Error(err) + } +} + +func TestMain(m *testing.M) { + testutil.RunAsRoot() + os.Exit(m.Run()) +} diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index 95926f5f9..fd4eee546 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -31,6 +31,23 @@ import ( "gvisor.googlesource.com/gvisor/runsc/specutils" ) +var caps = []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_SYS_CHROOT", +} + +// goferCaps is the minimal set of capabilities needed by the Gofer to operate +// on files. +var goferCaps = &specs.LinuxCapabilities{ + Bounding: caps, + Effective: caps, + Permitted: caps, +} + // Gofer implements subcommands.Command for the "gofer" command, which starts a // filesystem gofer. This command should not be called directly. type Gofer struct { @@ -72,25 +89,11 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } if g.applyCaps { - // Minimal set of capabilities needed by the Gofer to operate on files. - caps := []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_DAC_READ_SEARCH", - "CAP_FOWNER", - "CAP_FSETID", - } - lc := &specs.LinuxCapabilities{ - Bounding: caps, - Effective: caps, - Permitted: caps, - } - // Disable caps when calling myself again. // Note: minimal argument handling for the default case to keep it simple. args := os.Args args = append(args, "--apply-caps=false") - if err := setCapsAndCallSelf(args, lc); err != nil { + if err := setCapsAndCallSelf(args, goferCaps); err != nil { Fatalf("Unable to apply caps: %v", err) } panic("unreachable") diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index fdc9007e0..daf10b875 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -392,3 +392,13 @@ func Mount(src, dst, typ string, flags uint32) error { } return nil } + +// ContainsStr returns true if 'str' is inside 'strs'. +func ContainsStr(strs []string, str string) bool { + for _, s := range strs { + if s == str { + return true + } + } + return false +} -- cgit v1.2.3 From cf226d48ce8c49409049e03ed405366db9fc2a04 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Fri, 28 Sep 2018 09:43:13 -0700 Subject: Switch to root in userns when CAP_SYS_CHROOT is also missing Some tests check current capabilities and re-run the tests as root inside userns if required capabibilities are missing. It was checking for CAP_SYS_ADMIN only, CAP_SYS_CHROOT is also required now. PiperOrigin-RevId: 214949226 Change-Id: Ic81363969fa76c04da408fae8ea7520653266312 --- runsc/cmd/capability.go | 6 +++++- runsc/cmd/cmd.go | 1 - runsc/sandbox/BUILD | 1 + runsc/sandbox/sandbox.go | 7 ++++--- runsc/specutils/namespace.go | 22 +++++++--------------- runsc/test/testutil/BUILD | 1 + runsc/test/testutil/testutil.go | 11 ++++++----- 7 files changed, 24 insertions(+), 25 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/cmd/capability.go b/runsc/cmd/capability.go index affbb7ce3..0b18c5481 100644 --- a/runsc/cmd/capability.go +++ b/runsc/cmd/capability.go @@ -60,7 +60,11 @@ func applyCaps(caps *specs.LinuxCapabilities) error { newCaps.Set(c, set...) } - return newCaps.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS) + if err := newCaps.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS); err != nil { + return err + } + log.Infof("Capabilities applied: %+v", newCaps) + return nil } func getCaps(which capability.CapType, caps *specs.LinuxCapabilities) []string { diff --git a/runsc/cmd/cmd.go b/runsc/cmd/cmd.go index 44ebd7165..2937ae1c4 100644 --- a/runsc/cmd/cmd.go +++ b/runsc/cmd/cmd.go @@ -85,7 +85,6 @@ func setCapsAndCallSelf(args []string, caps *specs.LinuxCapabilities) error { return err } - log.Infof("Capabilities applied: %+v", caps) log.Infof("Execve %q again, bye!", binPath) syscall.Exec(binPath, args, []string{}) panic("unreachable") diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index 7ae19ff35..09965dcc0 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -25,6 +25,7 @@ go_library( "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", "@com_github_vishvananda_netlink//:go_default_library", ], ) diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index ef85f175f..d288be1d2 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -26,6 +26,7 @@ import ( "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" "gvisor.googlesource.com/gvisor/pkg/control/client" "gvisor.googlesource.com/gvisor/pkg/control/server" "gvisor.googlesource.com/gvisor/pkg/log" @@ -415,7 +416,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // as user nobody. if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { log.Warningf("Running sandbox in test mode as current user (uid=%d gid=%d). This is only safe in tests!", os.Getuid(), os.Getgid()) - } else if specutils.CanSetUIDGID() { + } else if specutils.HasCapabilities(capability.CAP_SETUID, capability.CAP_SETGID) { // Map nobody in the new namespace to nobody in the parent namespace. const nobody = 65534 cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ @@ -442,7 +443,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // bind-mount the executable inside it. if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { log.Warningf("Running sandbox in test mode without chroot. This is only safe in tests!") - } else if specutils.HasCapSysAdmin() { + } else if specutils.HasCapabilities(capability.CAP_SYS_ADMIN, capability.CAP_SYS_CHROOT) { log.Infof("Sandbox will be started in minimal chroot") chroot, err := setUpChroot() if err != nil { @@ -453,7 +454,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund cmd.Args[0] = "/runsc" cmd.Path = "/runsc" } else { - return fmt.Errorf("can't run sandbox process in minimal chroot since we don't have CAP_SYS_ADMIN") + return fmt.Errorf("can't run sandbox process in minimal chroot since we don't have CAP_SYS_ADMIN and CAP_SYS_CHROOT") } } diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go index 48a199a77..00293d45b 100644 --- a/runsc/specutils/namespace.go +++ b/runsc/specutils/namespace.go @@ -204,8 +204,8 @@ func SetUIDGIDMappings(cmd *exec.Cmd, s *specs.Spec) { } } -// CanSetUIDGID returns true if the user has SETUID and SETGID capabilities. -func CanSetUIDGID() bool { +// HasCapabilities returns true if the user has all capabilties in 'cs'. +func HasCapabilities(cs ...capability.Cap) bool { caps, err := capability.NewPid2(os.Getpid()) if err != nil { return false @@ -213,18 +213,10 @@ func CanSetUIDGID() bool { if err := caps.Load(); err != nil { return false } - return caps.Get(capability.EFFECTIVE, capability.CAP_SETUID) && - caps.Get(capability.EFFECTIVE, capability.CAP_SETGID) -} - -// HasCapSysAdmin returns true if the user has CAP_SYS_ADMIN capability. -func HasCapSysAdmin() bool { - caps, err := capability.NewPid2(os.Getpid()) - if err != nil { - return false - } - if err := caps.Load(); err != nil { - return false + for _, c := range cs { + if !caps.Get(capability.EFFECTIVE, c) { + return false + } } - return caps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) + return true } diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index 03ab3c4ac..ca91e07ff 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -18,5 +18,6 @@ go_library( "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", ], ) diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 37927f395..706db74a7 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -32,6 +32,7 @@ import ( "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -234,12 +235,12 @@ func WaitForHTTP(port int, timeout time.Duration) error { return Poll(cb, timeout) } -// RunAsRoot ensures the test runs with CAP_SYS_ADMIN. If need it will create -// a new user namespace and reexecute the test as root inside of the namespace. -// This functionr returns when it's running as root. If it needs to create -// another process, it will exit from there and not return. +// RunAsRoot ensures the test runs with CAP_SYS_ADMIN and CAP_SYS_CHROOT. If +// need it will create a new user namespace and reexecute the test as root +// inside of the namespace. This functionr returns when it's running as root. If +// it needs to create another process, it will exit from there and not return. func RunAsRoot() { - if specutils.HasCapSysAdmin() { + if specutils.HasCapabilities(capability.CAP_SYS_ADMIN, capability.CAP_SYS_CHROOT) { return } -- cgit v1.2.3 From 29cd05a7c66ee8061c0e5cf8e94c4e507dcf33e0 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Wed, 10 Oct 2018 08:59:25 -0700 Subject: Add sandbox to cgroup Sandbox creation uses the limits and reservations configured in the OCI spec and set cgroup options accordinly. Then it puts both the sandbox and gofer processes inside the cgroup. It also allows the cgroup to be pre-configured by the caller. If the cgroup already exists, sandbox and gofer processes will join the cgroup but it will not modify the cgroup with spec limits. PiperOrigin-RevId: 216538209 Change-Id: If2c65ffedf55820baab743a0edcfb091b89c1019 --- runsc/boot/loader.go | 90 ++++--- runsc/boot/loader_test.go | 11 +- runsc/cgroup/BUILD | 24 ++ runsc/cgroup/cgroup.go | 405 +++++++++++++++++++++++++++++ runsc/cgroup/cgroup_test.go | 56 ++++ runsc/cmd/boot.go | 23 +- runsc/container/container.go | 16 +- runsc/sandbox/BUILD | 1 + runsc/sandbox/sandbox.go | 57 +++- runsc/specutils/BUILD | 1 - runsc/specutils/cpu.go | 90 ------- runsc/specutils/specutils.go | 37 +++ runsc/test/integration/BUILD | 4 +- runsc/test/integration/integration_test.go | 84 ++++++ runsc/test/testutil/docker.go | 9 + 15 files changed, 776 insertions(+), 132 deletions(-) create mode 100644 runsc/cgroup/BUILD create mode 100644 runsc/cgroup/cgroup.go create mode 100644 runsc/cgroup/cgroup_test.go delete mode 100644 runsc/specutils/cpu.go (limited to 'runsc/specutils') diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 1ad6b09f4..dc3c6c3d0 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -20,6 +20,7 @@ import ( "math/rand" "os" "os/signal" + "runtime" "sync" "sync/atomic" "syscall" @@ -138,14 +139,39 @@ func init() { kernel.RegisterSyscallTable(slinux.AMD64) } +// Args are the arguments for New(). +type Args struct { + // Id is the sandbox ID. + ID string + // Spec is the sandbox specification. + Spec *specs.Spec + // Conf is the system configuration. + Conf *Config + // ControllerFD is the FD to the URPC controller. + ControllerFD int + // DeviceFD is an optional argument that is passed to the platform. + DeviceFD int + // GoferFDs is an array of FDs used to connect with the Gofer. + GoferFDs []int + // StdioFDs is the stdio for the application. + StdioFDs []int + // Console is set to true if using TTY. + Console bool + // NumCPU is the number of CPUs to create inside the sandbox. + NumCPU int + // TotalMem is the initial amount of total memory to report back to the + // container. + TotalMem uint64 +} + // New initializes a new kernel loader configured by spec. // New also handles setting up a kernel for restoring a container. -func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, goferFDs []int, stdioFDs []int, console bool) (*Loader, error) { +func New(args Args) (*Loader, error) { if err := usage.Init(); err != nil { return nil, fmt.Errorf("error setting up memory usage: %v", err) } // Create kernel and platform. - p, err := createPlatform(conf, deviceFD) + p, err := createPlatform(args.Conf, args.DeviceFD) if err != nil { return nil, fmt.Errorf("error creating platform: %v", err) } @@ -168,7 +194,7 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, } tk.SetClocks(time.NewCalibratedClocks()) - if err := enableStrace(conf); err != nil { + if err := enableStrace(args.Conf); err != nil { return nil, fmt.Errorf("failed to enable strace: %v", err) } @@ -176,35 +202,41 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, // this point. Netns is configured before Run() is called. Netstack is // configured using a control uRPC message. Host network is configured inside // Run(). - networkStack, err := newEmptyNetworkStack(conf, k) + networkStack, err := newEmptyNetworkStack(args.Conf, k) if err != nil { return nil, fmt.Errorf("failed to create network: %v", err) } // Create capabilities. - caps, err := specutils.Capabilities(spec.Process.Capabilities) + caps, err := specutils.Capabilities(args.Spec.Process.Capabilities) if err != nil { return nil, fmt.Errorf("error creating capabilities: %v", err) } // Convert the spec's additional GIDs to KGIDs. - extraKGIDs := make([]auth.KGID, 0, len(spec.Process.User.AdditionalGids)) - for _, GID := range spec.Process.User.AdditionalGids { + extraKGIDs := make([]auth.KGID, 0, len(args.Spec.Process.User.AdditionalGids)) + for _, GID := range args.Spec.Process.User.AdditionalGids { extraKGIDs = append(extraKGIDs, auth.KGID(GID)) } // Create credentials. creds := auth.NewUserCredentials( - auth.KUID(spec.Process.User.UID), - auth.KGID(spec.Process.User.GID), + auth.KUID(args.Spec.Process.User.UID), + auth.KGID(args.Spec.Process.User.GID), extraKGIDs, caps, auth.NewRootUserNamespace()) - // Get CPU numbers from spec. - cpuNum, err := specutils.CalculateCPUNumber(spec) - if err != nil { - return nil, fmt.Errorf("cannot get cpus from spec: %v", err) + if args.NumCPU == 0 { + args.NumCPU = runtime.NumCPU() + } + log.Infof("CPUs: %d", args.NumCPU) + + if args.TotalMem > 0 { + // Adjust the total memory returned by the Sentry so that applications that + // use /proc/meminfo can make allocations based on this limit. + usage.MinimumTotalMemoryBytes = args.TotalMem + log.Infof("Setting total memory to %.2f GB", float64(args.TotalMem)/(2^30)) } // Initiate the Kernel object, which is required by the Context passed @@ -214,9 +246,9 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, Timekeeper: tk, RootUserNamespace: creds.UserNamespace, NetworkStack: networkStack, - ApplicationCores: uint(cpuNum), + ApplicationCores: uint(args.NumCPU), Vdso: vdso, - RootUTSNamespace: kernel.NewUTSNamespace(spec.Hostname, "", creds.UserNamespace), + RootUTSNamespace: kernel.NewUTSNamespace(args.Spec.Hostname, "", creds.UserNamespace), RootIPCNamespace: kernel.NewIPCNamespace(creds.UserNamespace), RootAbstractSocketNamespace: kernel.NewAbstractSocketNamespace(), }); err != nil { @@ -224,7 +256,7 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, } // Turn on packet logging if enabled. - if conf.LogPackets { + if args.Conf.LogPackets { log.Infof("Packet logging enabled") atomic.StoreUint32(&sniffer.LogPackets, 1) } else { @@ -233,7 +265,7 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, } // Create a watchdog. - watchdog := watchdog.New(k, watchdog.DefaultTimeout, conf.WatchdogAction) + watchdog := watchdog.New(k, watchdog.DefaultTimeout, args.Conf.WatchdogAction) // Create the control server using the provided FD. // @@ -244,7 +276,7 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, // misconfigured process will cause an error, and we want the control // server up before that so that we don't time out trying to connect to // it. - ctrl, err := newController(controllerFD, k, watchdog) + ctrl, err := newController(args.ControllerFD, k, watchdog) if err != nil { return nil, fmt.Errorf("error creating control server: %v", err) } @@ -255,20 +287,20 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, return nil, fmt.Errorf("failed to ignore child stop signals: %v", err) } // Ensure that signals received are forwarded to the emulated kernel. - ps := syscall.Signal(conf.PanicSignal) + ps := syscall.Signal(args.Conf.PanicSignal) startSignalForwarding := sighandling.PrepareForwarding(k, ps) - if conf.PanicSignal != -1 { - // Panics if the sentry receives 'conf.PanicSignal'. + if args.Conf.PanicSignal != -1 { + // Panics if the sentry receives 'Config.PanicSignal'. panicChan := make(chan os.Signal, 1) signal.Notify(panicChan, ps) go func() { // S/R-SAFE: causes sentry panic. <-panicChan panic("Signal-induced panic") }() - log.Infof("Panic signal set to %v(%d)", ps, conf.PanicSignal) + log.Infof("Panic signal set to %v(%d)", ps, args.Conf.PanicSignal) } - procArgs, err := newProcess(id, spec, creds, k) + procArgs, err := newProcess(args.ID, args.Spec, creds, k) if err != nil { return nil, fmt.Errorf("failed to create root process: %v", err) } @@ -276,15 +308,15 @@ func New(id string, spec *specs.Spec, conf *Config, controllerFD, deviceFD int, l := &Loader{ k: k, ctrl: ctrl, - conf: conf, - console: console, + conf: args.Conf, + console: args.Console, watchdog: watchdog, - spec: spec, - goferFDs: goferFDs, - stdioFDs: stdioFDs, + spec: args.Spec, + goferFDs: args.GoferFDs, + stdioFDs: args.StdioFDs, startSignalForwarding: startSignalForwarding, rootProcArgs: procArgs, - sandboxID: id, + sandboxID: args.ID, processes: make(map[execID]*execProcess), } ctrl.manager.l = l diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index ea8411a8b..10efa4427 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -102,7 +102,16 @@ func createLoader() (*Loader, func(), error) { } stdio := []int{int(os.Stdin.Fd()), int(os.Stdout.Fd()), int(os.Stderr.Fd())} - l, err := New("foo", spec, conf, fd, -1 /* device fd */, []int{sandEnd}, stdio, false) + args := Args{ + ID: "foo", + Spec: spec, + Conf: conf, + ControllerFD: fd, + DeviceFD: -1, + GoferFDs: []int{sandEnd}, + StdioFDs: stdio, + } + l, err := New(args) if err != nil { cleanup() return nil, nil, err diff --git a/runsc/cgroup/BUILD b/runsc/cgroup/BUILD new file mode 100644 index 000000000..4a535d230 --- /dev/null +++ b/runsc/cgroup/BUILD @@ -0,0 +1,24 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cgroup", + srcs = ["cgroup.go"], + importpath = "gvisor.googlesource.com/gvisor/runsc/cgroup", + visibility = [ + "//runsc:__subpackages__", + ], + deps = [ + "//pkg/log", + "//runsc/specutils", + "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + ], +) + +go_test( + name = "cgroup_test", + size = "small", + srcs = ["cgroup_test.go"], + embed = [":cgroup"], +) diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go new file mode 100644 index 000000000..6a0092be8 --- /dev/null +++ b/runsc/cgroup/cgroup.go @@ -0,0 +1,405 @@ +// Copyright 2018 Google Inc. +// +// 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 cgroup provides an interface to read and write configuration to +// cgroup. +package cgroup + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/specutils" +) + +const ( + cgroupRoot = "/sys/fs/cgroup" +) + +var controllers = map[string]controller{ + "blkio": &blockIO{}, + "cpu": &cpu{}, + "cpuset": &cpuSet{}, + "memory": &memory{}, + "net_cls": &networkClass{}, + "net_prio": &networkPrio{}, + + // These controllers either don't have anything in the OCI spec or is + // irrevalant for a sandbox, e.g. pids. + "devices": &noop{}, + "freezer": &noop{}, + "perf_event": &noop{}, + "pids": &noop{}, + "systemd": &noop{}, +} + +func setOptionalValueInt(path, name string, val *int64) error { + if val == nil || *val == 0 { + return nil + } + str := strconv.FormatInt(*val, 10) + return setValue(path, name, str) +} + +func setOptionalValueUint(path, name string, val *uint64) error { + if val == nil || *val == 0 { + return nil + } + str := strconv.FormatUint(*val, 10) + return setValue(path, name, str) +} + +func setOptionalValueUint32(path, name string, val *uint32) error { + if val == nil || *val == 0 { + return nil + } + str := strconv.FormatUint(uint64(*val), 10) + return setValue(path, name, str) +} + +func setOptionalValueUint16(path, name string, val *uint16) error { + if val == nil || *val == 0 { + return nil + } + str := strconv.FormatUint(uint64(*val), 10) + return setValue(path, name, str) +} + +func setValue(path, name, data string) error { + fullpath := filepath.Join(path, name) + return ioutil.WriteFile(fullpath, []byte(data), 0700) +} + +func getValue(path, name string) (string, error) { + fullpath := filepath.Join(path, name) + out, err := ioutil.ReadFile(fullpath) + if err != nil { + return "", err + } + return string(out), nil +} + +// fillFromAncestor sets the value of a cgroup file from the first ancestor +// that has content. It does nothing if the file in 'path' has already been set. +func fillFromAncestor(path string) (string, error) { + out, err := ioutil.ReadFile(path) + if err != nil { + return "", err + } + val := strings.TrimSpace(string(out)) + if val != "" { + // File is set, stop here. + return val, nil + } + + // File is not set, recurse to parent and then set here. + name := filepath.Base(path) + parent := filepath.Dir(filepath.Dir(path)) + val, err = fillFromAncestor(filepath.Join(parent, name)) + if err != nil { + return "", err + } + if err := ioutil.WriteFile(path, []byte(val), 0700); err != nil { + return "", err + } + return val, nil +} + +func countCpuset(cpuset string) (int, error) { + var count int + for _, p := range strings.Split(cpuset, ",") { + interval := strings.Split(p, "-") + switch len(interval) { + case 1: + if _, err := strconv.Atoi(interval[0]); err != nil { + return 0, err + } + count++ + + case 2: + start, err := strconv.Atoi(interval[0]) + if err != nil { + return 0, err + } + end, err := strconv.Atoi(interval[1]) + if err != nil { + return 0, err + } + if start < 0 || end < 0 || start > end { + return 0, fmt.Errorf("invalid cpuset: %q", p) + } + count += end - start + 1 + + default: + return 0, fmt.Errorf("invalid cpuset: %q", p) + } + } + return count, nil +} + +// Cgroup represents a group inside all controllers. For example: Name='/foo/bar' +// maps to /sys/fs/cgroup//foo/bar on all controllers. +type Cgroup struct { + Name string `json:"name"` + Own bool `json:"own"` +} + +// New creates a new Cgroup instance if the spec includes a cgroup path. +// Otherwise it returns nil and false. +func New(spec *specs.Spec) (*Cgroup, bool) { + if spec.Linux == nil || spec.Linux.CgroupsPath == "" { + return nil, false + } + return &Cgroup{Name: spec.Linux.CgroupsPath}, true +} + +// Install creates and configures cgroups according to 'res'. If cgroup path +// already exists, it means that the caller has already provided a +// pre-configured cgroups, and 'res' is ignored. +func (c *Cgroup) Install(res *specs.LinuxResources) error { + if _, err := os.Stat(c.makePath("memory")); err == nil { + // If cgroup has already been created; it has been setup by caller. Don't + // make any changes to configuration, just join when sandbox/gofer starts. + log.Debugf("Using pre-created cgroup %q", c.Name) + return nil + } + + // Mark that cgroup resources are owned by me. + log.Debugf("Creating cgroup %q", c.Name) + c.Own = true + clean := specutils.MakeCleanup(func() { c.Uninstall() }) + defer clean.Clean() + + for key, ctrl := range controllers { + path := c.makePath(key) + if err := os.MkdirAll(path, 0755); err != nil { + return err + } + if res != nil { + if err := ctrl.set(res, path); err != nil { + return err + } + } + } + clean.Release() + return nil +} + +// Uninstall removes the settings done in Install(). If cgroup path already +// existed when Install() was called, Uninstall is a noop. +func (c *Cgroup) Uninstall() error { + if !c.Own { + // cgroup is managed by caller, don't touch it. + return nil + } + log.Debugf("Deleting cgroup %q", c.Name) + for key := range controllers { + if err := syscall.Rmdir(c.makePath(key)); err != nil && !os.IsNotExist(err) { + return err + } + } + return nil +} + +// Add adds given process to all controllers. +func (c *Cgroup) Add(pid int) error { + for key := range controllers { + if err := setValue(c.makePath(key), "cgroup.procs", strconv.Itoa(pid)); err != nil { + return err + } + } + return nil +} + +// NumCPU returns the number of CPUs configured in 'cpuset/cpuset.cpus'. +func (c *Cgroup) NumCPU() (int, error) { + path := c.makePath("cpuset") + cpuset, err := getValue(path, "cpuset.cpus") + if err != nil { + return 0, err + } + return countCpuset(strings.TrimSpace(cpuset)) +} + +// MemoryLimit returns the memory limit. +func (c *Cgroup) MemoryLimit() (uint64, error) { + path := c.makePath("memory") + limStr, err := getValue(path, "memory.limit_in_bytes") + if err != nil { + return 0, err + } + return strconv.ParseUint(strings.TrimSpace(limStr), 10, 64) +} + +func (c *Cgroup) makePath(controllerName string) string { + return filepath.Join(cgroupRoot, controllerName, c.Name) +} + +type controller interface { + set(*specs.LinuxResources, string) error +} + +type noop struct{} + +func (*noop) set(*specs.LinuxResources, string) error { + return nil +} + +type memory struct{} + +func (*memory) set(spec *specs.LinuxResources, path string) error { + if spec.Memory == nil { + return nil + } + if err := setOptionalValueInt(path, "memory.limit_in_bytes", spec.Memory.Limit); err != nil { + return err + } + if err := setOptionalValueInt(path, "memory.soft_limit_in_bytes", spec.Memory.Reservation); err != nil { + return err + } + if err := setOptionalValueInt(path, "memory.memsw.limit_in_bytes", spec.Memory.Swap); err != nil { + return err + } + if err := setOptionalValueInt(path, "memory.kmem.limit_in_bytes", spec.Memory.Kernel); err != nil { + return err + } + if err := setOptionalValueInt(path, "memory.kmem.tcp.limit_in_bytes", spec.Memory.KernelTCP); err != nil { + return err + } + if err := setOptionalValueUint(path, "memory.swappiness", spec.Memory.Swappiness); err != nil { + return err + } + + if spec.Memory.DisableOOMKiller != nil && *spec.Memory.DisableOOMKiller { + if err := setValue(path, "memory.oom_control", "1"); err != nil { + return err + } + } + return nil +} + +type cpu struct{} + +func (*cpu) set(spec *specs.LinuxResources, path string) error { + if spec.CPU == nil { + return nil + } + if err := setOptionalValueUint(path, "cpu.shares", spec.CPU.Shares); err != nil { + return err + } + if err := setOptionalValueInt(path, "cpu.cfs_quota_us", spec.CPU.Quota); err != nil { + return err + } + return setOptionalValueUint(path, "cpu.cfs_period_us", spec.CPU.Period) +} + +type cpuSet struct{} + +func (*cpuSet) set(spec *specs.LinuxResources, path string) error { + // cpuset.cpus and mems are required fields, but are not set on a new cgroup. + // If not set in the spec, get it from one of the ancestors cgroup. + if spec.CPU == nil || spec.CPU.Cpus == "" { + if _, err := fillFromAncestor(filepath.Join(path, "cpuset.cpus")); err != nil { + return err + } + } else { + if err := setValue(path, "cpuset.cpus", spec.CPU.Cpus); err != nil { + return err + } + } + + if spec.CPU == nil || spec.CPU.Mems == "" { + _, err := fillFromAncestor(filepath.Join(path, "cpuset.mems")) + return err + } + mems := spec.CPU.Mems + return setValue(path, "cpuset.mems", mems) +} + +type blockIO struct{} + +func (*blockIO) set(spec *specs.LinuxResources, path string) error { + if spec.BlockIO == nil { + return nil + } + + if err := setOptionalValueUint16(path, "blkio.weight", spec.BlockIO.Weight); err != nil { + return err + } + if err := setOptionalValueUint16(path, "blkio.leaf_weight", spec.BlockIO.LeafWeight); err != nil { + return err + } + + for _, dev := range spec.BlockIO.WeightDevice { + val := fmt.Sprintf("%d:%d %d", dev.Major, dev.Minor, dev.Weight) + if err := setValue(path, "blkio.weight_device", val); err != nil { + return err + } + val = fmt.Sprintf("%d:%d %d", dev.Major, dev.Minor, dev.LeafWeight) + if err := setValue(path, "blkio.leaf_weight_device", val); err != nil { + return err + } + } + if err := setThrottle(path, "blkio.throttle.read_bps_device", spec.BlockIO.ThrottleReadBpsDevice); err != nil { + return err + } + if err := setThrottle(path, "blkio.throttle.write_bps_device", spec.BlockIO.ThrottleWriteBpsDevice); err != nil { + return err + } + if err := setThrottle(path, "blkio.throttle.read_iops_device", spec.BlockIO.ThrottleReadIOPSDevice); err != nil { + return err + } + return setThrottle(path, "blkio.throttle.write_iops_device", spec.BlockIO.ThrottleWriteIOPSDevice) +} + +func setThrottle(path, name string, devs []specs.LinuxThrottleDevice) error { + for _, dev := range devs { + val := fmt.Sprintf("%d:%d %d", dev.Major, dev.Minor, dev.Rate) + if err := setValue(path, name, val); err != nil { + return err + } + } + return nil +} + +type networkClass struct{} + +func (*networkClass) set(spec *specs.LinuxResources, path string) error { + if spec.Network == nil { + return nil + } + return setOptionalValueUint32(path, "net_cls.classid", spec.Network.ClassID) +} + +type networkPrio struct{} + +func (*networkPrio) set(spec *specs.LinuxResources, path string) error { + if spec.Network == nil { + return nil + } + for _, prio := range spec.Network.Priorities { + val := fmt.Sprintf("%s %d", prio.Name, prio.Priority) + if err := setValue(path, "net_prio.ifpriomap", val); err != nil { + return err + } + } + return nil +} diff --git a/runsc/cgroup/cgroup_test.go b/runsc/cgroup/cgroup_test.go new file mode 100644 index 000000000..cde915329 --- /dev/null +++ b/runsc/cgroup/cgroup_test.go @@ -0,0 +1,56 @@ +// Copyright 2018 Google Inc. +// +// 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 cgroup + +import ( + "testing" +) + +func TestCountCpuset(t *testing.T) { + for _, tc := range []struct { + str string + want int + error bool + }{ + {str: "0", want: 1}, + {str: "0,1,2,8,9,10", want: 6}, + {str: "0-1", want: 2}, + {str: "0-7", want: 8}, + {str: "0-7,16,32-39,64,65", want: 19}, + {str: "a", error: true}, + {str: "5-a", error: true}, + {str: "a-5", error: true}, + {str: "-10", error: true}, + {str: "15-", error: true}, + {str: "-", error: true}, + {str: "--", error: true}, + } { + t.Run(tc.str, func(t *testing.T) { + got, err := countCpuset(tc.str) + if tc.error { + if err == nil { + t.Errorf("countCpuset(%q) should have failed", tc.str) + } + } else { + if err != nil { + t.Errorf("countCpuset(%q) failed: %v", tc.str, err) + } + if tc.want != got { + t.Errorf("countCpuset(%q) want: %d, got: %d", tc.str, tc.want, got) + } + } + }) + } +} diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index c6f78f63f..d26e92bcd 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -59,6 +59,13 @@ type Boot struct { // applyCaps determines if capabilities defined in the spec should be applied // to the process. applyCaps bool + + // cpuNum number of CPUs to create inside the sandbox. + cpuNum int + + // totalMem sets the initial amount of total memory to report back to the + // container. + totalMem uint64 } // Name implements subcommands.Command.Name. @@ -86,6 +93,8 @@ func (b *Boot) SetFlags(f *flag.FlagSet) { f.Var(&b.stdioFDs, "stdio-fds", "list of FDs containing sandbox stdin, stdout, and stderr in that order") f.BoolVar(&b.console, "console", false, "set to true if the sandbox should allow terminal ioctl(2) syscalls") f.BoolVar(&b.applyCaps, "apply-caps", false, "if true, apply capabilities defined in the spec to the process") + f.IntVar(&b.cpuNum, "cpu-num", 0, "number of CPUs to create inside the sandbox") + f.Uint64Var(&b.totalMem, "total-memory", 0, "sets the initial amount of total memory to report back to the container") } // Execute implements subcommands.Command.Execute. It starts a sandbox in a @@ -143,7 +152,19 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } // Create the loader. - l, err := boot.New(f.Arg(0), spec, conf, b.controllerFD, b.deviceFD, b.ioFDs.GetArray(), b.stdioFDs.GetArray(), b.console) + bootArgs := boot.Args{ + ID: f.Arg(0), + Spec: spec, + Conf: conf, + ControllerFD: b.controllerFD, + DeviceFD: b.deviceFD, + GoferFDs: b.ioFDs.GetArray(), + StdioFDs: b.stdioFDs.GetArray(), + Console: b.console, + NumCPU: b.cpuNum, + TotalMem: b.totalMem, + } + l, err := boot.New(bootArgs) if err != nil { Fatalf("error creating loader: %v", err) } diff --git a/runsc/container/container.go b/runsc/container/container.go index f0cdee8d3..eaa62daf1 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -262,6 +262,8 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo Status: Creating, Owner: os.Getenv("USER"), } + cu := specutils.MakeCleanup(func() { c.Destroy() }) + defer cu.Clean() // If the metadata annotations indicate that this container should be // started in an existing sandbox, we must do so. The metadata will @@ -276,12 +278,13 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // Start a new sandbox for this container. Any errors after this point // must destroy the container. - s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket, ioFiles) + c.Sandbox, err = sandbox.Create(id, spec, conf, bundleDir, consoleSocket, ioFiles) if err != nil { - c.Destroy() return nil, err } - c.Sandbox = s + if err := c.Sandbox.AddGoferToCgroup(c.GoferPid); err != nil { + return nil, err + } } else { // This is sort of confusing. For a sandbox with a root // container and a child container in it, runsc sees: @@ -300,7 +303,6 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // Find the sandbox associated with this ID. sb, err := Load(conf.RootDir, sbid) if err != nil { - c.Destroy() return nil, err } c.Sandbox = sb.Sandbox @@ -309,7 +311,6 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // Save the metadata file. if err := c.save(); err != nil { - c.Destroy() return nil, err } @@ -317,11 +318,11 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // this file is created, so it must be the last thing we do. if pidFile != "" { if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(c.SandboxPid())), 0644); err != nil { - c.Destroy() return nil, fmt.Errorf("error writing PID file: %v", err) } } + cu.Release() return c, nil } @@ -358,6 +359,9 @@ func (c *Container) Start(conf *boot.Config) error { if err := c.Sandbox.Start(c.Spec, conf, c.ID, ioFiles); err != nil { return err } + if err := c.Sandbox.AddGoferToCgroup(c.GoferPid); err != nil { + return err + } } // "If any poststart hook fails, the runtime MUST log a warning, but diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index 09965dcc0..eb9c4cd76 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -21,6 +21,7 @@ go_library( "//pkg/sentry/platform/kvm", "//pkg/urpc", "//runsc/boot", + "//runsc/cgroup", "//runsc/console", "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 847417a15..26d725bdd 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -34,6 +34,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/platform/kvm" "gvisor.googlesource.com/gvisor/pkg/urpc" "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/cgroup" "gvisor.googlesource.com/gvisor/runsc/console" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -58,12 +59,26 @@ type Sandbox struct { // Chroot is the path to the chroot directory that the sandbox process // is running in. Chroot string `json:"chroot"` + + // Ccroup has the cgroup configuration for the sandbox. + Cgroup *cgroup.Cgroup `json:"cgroup"` } // Create creates the sandbox process. The caller must call Destroy() on the // sandbox. func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string, ioFiles []*os.File) (*Sandbox, error) { s := &Sandbox{ID: id} + c := specutils.MakeCleanup(func() { s.destroy() }) + defer c.Clean() + + if cg, ok := cgroup.New(spec); ok { + s.Cgroup = cg + + // If there is cgroup config, install it before creating sandbox process. + if err := s.Cgroup.Install(spec.Linux.Resources); err != nil { + return nil, fmt.Errorf("error configuring cgroup: %v", err) + } + } // Create the sandbox process. if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, ioFiles); err != nil { @@ -75,6 +90,13 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo return nil, err } + if s.Cgroup != nil { + if err := s.Cgroup.Add(s.Pid); err != nil { + return nil, fmt.Errorf("error adding sandbox to cgroup: %v", err) + } + } + + c.Release() return s, nil } @@ -483,6 +505,24 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } } + if s.Cgroup != nil { + cpuNum, err := s.Cgroup.NumCPU() + if err != nil { + return fmt.Errorf("error getting cpu count from cgroups: %v", err) + } + cmd.Args = append(cmd.Args, "--cpu-num", strconv.Itoa(cpuNum)) + + mem, err := s.Cgroup.MemoryLimit() + if err != nil { + return fmt.Errorf("error getting memory limit from cgroups: %v", err) + } + // When memory limit is unset, a "large" number is returned. In that case, + // just stick with the default. + if mem < 0x7ffffffffffff000 { + cmd.Args = append(cmd.Args, "--total-memory", strconv.FormatUint(mem, 10)) + } + } + // Add container as the last argument. cmd.Args = append(cmd.Args, s.ID) @@ -590,8 +630,15 @@ func (s *Sandbox) destroy() error { } } + if s.Cgroup != nil { + if err := s.Cgroup.Uninstall(); err != nil { + return err + } + } if s.Chroot != "" { - return tearDownChroot(s.Chroot) + if err := tearDownChroot(s.Chroot); err != nil { + return err + } } return nil @@ -761,6 +808,14 @@ func (s *Sandbox) waitForStopped() error { return backoff.Retry(op, b) } +// AddGoferToCgroup adds the gofer process to the sandbox's cgroup. +func (s *Sandbox) AddGoferToCgroup(pid int) error { + if s.Cgroup != nil { + return s.Cgroup.Add(pid) + } + return nil +} + // deviceFileForPlatform opens the device file for the given platform. If the // platform does not need a device file, then nil is returned. func deviceFileForPlatform(p boot.PlatformType) (*os.File, error) { diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index f1a99ce48..e73b2293f 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -5,7 +5,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "specutils", srcs = [ - "cpu.go", "namespace.go", "specutils.go", ], diff --git a/runsc/specutils/cpu.go b/runsc/specutils/cpu.go deleted file mode 100644 index 9abe26b64..000000000 --- a/runsc/specutils/cpu.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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 specutils - -import ( - "fmt" - "runtime" - "strconv" - "strings" - - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// CalculateCPUNumber calculates the number of CPUs that should be exposed -// inside the sandbox. -func CalculateCPUNumber(spec *specs.Spec) (int, error) { - // If spec does not contain CPU field, then return the number of host CPUs. - if spec == nil || spec.Linux == nil || spec.Linux.Resources == nil || spec.Linux.Resources.CPU == nil { - return runtime.NumCPU(), nil - } - cpuSpec := spec.Linux.Resources.CPU - - // If cpuSpec.Cpus is specified, then parse and return that. They must be in - // the list format for cpusets, which is "a comma-separated list of CPU - // numbers and ranges of numbers, in ASCII decimal." --man 7 cpuset. - cpus := cpuSpec.Cpus - if cpus != "" { - cpuNum := 0 - for _, subs := range strings.Split(cpus, ",") { - result, err := parseCPUNumber(subs) - if err != nil { - return 0, err - } - cpuNum += result - } - return cpuNum, nil - } - - // If CPU.Quota and CPU.Period are specified, we can divide them to get an - // approximation of the number of CPUs needed. - if cpuSpec.Quota != nil && cpuSpec.Period != nil && *cpuSpec.Period != 0 { - cpuQuota := *cpuSpec.Quota - cpuPeriod := *cpuSpec.Period - return int(cpuQuota)/int(cpuPeriod) + 1, nil - } - - // Default to number of host cpus. - return runtime.NumCPU(), nil -} - -// parseCPUNumber converts a cpuset string into the number of cpus included in -// the string , e.g. "3-6" -> 4. -func parseCPUNumber(cpus string) (int, error) { - switch cpusSlice := strings.Split(cpus, "-"); len(cpusSlice) { - case 1: - // cpus is not a range. We must only check that it is a valid number. - if _, err := strconv.Atoi(cpus); err != nil { - return 0, fmt.Errorf("invalid individual cpu number %q", cpus) - } - return 1, nil - case 2: - // cpus is a range. We must check that start and end are valid numbers, - // and calculate their difference (inclusively). - first, err := strconv.Atoi(cpusSlice[0]) - if err != nil || first < 0 { - return 0, fmt.Errorf("invalid first cpu number %q in range %q", cpusSlice[0], cpus) - } - last, err := strconv.Atoi(cpusSlice[1]) - if err != nil || last < 0 { - return 0, fmt.Errorf("invalid last cpu number %q in range %q", cpusSlice[1], cpus) - } - cpuNum := last - first + 1 - if cpuNum <= 0 { - return 0, fmt.Errorf("cpu range %q does not include positive number of cpus", cpus) - } - } - return 0, fmt.Errorf("invalid cpu string %q", cpus) -} diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index daf10b875..ac017ba2d 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -43,6 +43,13 @@ func LogSpec(spec *specs.Spec) { log.Debugf("Spec: %+v", spec) log.Debugf("Spec.Hooks: %+v", spec.Hooks) log.Debugf("Spec.Linux: %+v", spec.Linux) + if spec.Linux != nil && spec.Linux.Resources != nil { + res := spec.Linux.Resources + log.Debugf("Spec.Linux.Resources.Memory: %+v", res.Memory) + log.Debugf("Spec.Linux.Resources.CPU: %+v", res.CPU) + log.Debugf("Spec.Linux.Resources.BlockIO: %+v", res.BlockIO) + log.Debugf("Spec.Linux.Resources.Network: %+v", res.Network) + } log.Debugf("Spec.Process: %+v", spec.Process) log.Debugf("Spec.Root: %+v", spec.Root) } @@ -402,3 +409,33 @@ func ContainsStr(strs []string, str string) bool { } return false } + +// Cleanup allows defers to be aborted when cleanup needs to happen +// conditionally. Usage: +// c := MakeCleanup(func() { f.Close() }) +// defer c.Clean() // any failure before release is called will close the file. +// ... +// c.Release() // on success, aborts closing the file and return it. +// return f +type Cleanup struct { + clean func() + released bool +} + +// MakeCleanup creates a new Cleanup object. +func MakeCleanup(f func()) Cleanup { + return Cleanup{clean: f} +} + +// Clean calls the cleanup function. +func (c *Cleanup) Clean() { + if !c.released { + c.clean() + } +} + +// Release releases the cleanup from its duties, i.e. cleanup function is not +// called after this point. +func (c *Cleanup) Release() { + c.released = true +} diff --git a/runsc/test/integration/BUILD b/runsc/test/integration/BUILD index 4407016ad..726ebf49e 100644 --- a/runsc/test/integration/BUILD +++ b/runsc/test/integration/BUILD @@ -15,9 +15,7 @@ go_test( "manual", "local", ], - deps = [ - "//runsc/test/testutil", - ], + deps = ["//runsc/test/testutil"], ) go_library( diff --git a/runsc/test/integration/integration_test.go b/runsc/test/integration/integration_test.go index 5f24aeed5..5480c5bbe 100644 --- a/runsc/test/integration/integration_test.go +++ b/runsc/test/integration/integration_test.go @@ -26,6 +26,7 @@ import ( "net" "net/http" "os" + "strconv" "strings" "testing" "time" @@ -179,6 +180,89 @@ func TestConnectToSelf(t *testing.T) { } } +func TestMemLimit(t *testing.T) { + if err := testutil.Pull("alpine"); err != nil { + t.Fatal("docker pull failed:", err) + } + d := testutil.MakeDocker("cgroup-test") + cmd := "cat /proc/meminfo | grep MemTotal: | awk '{print $2}'" + out, err := d.RunFg("--memory=500MB", "alpine", "sh", "-c", cmd) + if err != nil { + t.Fatal("docker run failed:", err) + } + defer d.CleanUp() + + // Remove warning message that swap isn't present. + if strings.HasPrefix(out, "WARNING") { + lines := strings.Split(out, "\n") + if len(lines) != 3 { + t.Fatalf("invalid output: %s", out) + } + out = lines[1] + } + + got, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64) + if err != nil { + t.Fatalf("failed to parse %q: %v", out, err) + } + if want := uint64(500 * 1024); got != want { + t.Errorf("MemTotal got: %d, want: %d", got, want) + } +} + +func TestNumCPU(t *testing.T) { + if err := testutil.Pull("alpine"); err != nil { + t.Fatal("docker pull failed:", err) + } + d := testutil.MakeDocker("cgroup-test") + cmd := "cat /proc/cpuinfo | grep 'processor.*:' | wc -l" + out, err := d.RunFg("--cpuset-cpus=0", "alpine", "sh", "-c", cmd) + if err != nil { + t.Fatal("docker run failed:", err) + } + defer d.CleanUp() + + got, err := strconv.Atoi(strings.TrimSpace(out)) + if err != nil { + t.Fatalf("failed to parse %q: %v", out, err) + } + if want := 1; got != want { + t.Errorf("MemTotal got: %d, want: %d", got, want) + } +} + +// TestCgroup sets cgroup options and checks that container can start. +// TODO: Verify that these were set to cgroup on the host. +func TestCgroup(t *testing.T) { + if err := testutil.Pull("alpine"); err != nil { + t.Fatal("docker pull failed:", err) + } + d := testutil.MakeDocker("cgroup-test") + + var args []string + args = append(args, "--cpu-shares=1000") + args = append(args, "--cpu-period=2000") + args = append(args, "--cpu-quota=3000") + args = append(args, "--cpuset-cpus=0") + args = append(args, "--cpuset-mems=0") + args = append(args, "--kernel-memory=100MB") + args = append(args, "--memory=1GB") + args = append(args, "--memory-reservation=500MB") + args = append(args, "--memory-swap=2GB") + args = append(args, "--memory-swappiness=5") + args = append(args, "--blkio-weight=750") + + args = append(args, "hello-world") + if err := d.Run(args...); err != nil { + t.Fatal("docker create failed:", err) + } + defer d.CleanUp() + + if _, err := d.WaitForOutput("Hello from Docker!", 5*time.Second); err != nil { + t.Fatalf("docker didn't say hello: %v", err) + } +} + func TestMain(m *testing.M) { testutil.EnsureSupportedDockerVersion() os.Exit(m.Run()) diff --git a/runsc/test/testutil/docker.go b/runsc/test/testutil/docker.go index d70b4377a..2f15ab818 100644 --- a/runsc/test/testutil/docker.go +++ b/runsc/test/testutil/docker.go @@ -198,6 +198,15 @@ func (d *Docker) Run(args ...string) error { return err } +// RunFg calls 'docker run' with the arguments provided in the foreground. It +// blocks until the container exits and returns the output. +func (d *Docker) RunFg(args ...string) (string, error) { + a := []string{"run", "--runtime", d.Runtime, "--name", d.Name} + a = append(a, args...) + out, err := do(a...) + return string(out), err +} + // Logs calls 'docker logs'. func (d *Docker) Logs() (string, error) { return do("logs", d.Name) -- cgit v1.2.3 From e68d86e1bd47f7905e4452f7ce0e04e683561f85 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 11 Oct 2018 14:28:15 -0700 Subject: Make debug log file name configurable This is a breaking change if you're using --debug-log-dir. The fix is to replace it with --debug-log and add a '/' at the end: --debug-log-dir=/tmp/runsc ==> --debug-log=/tmp/runsc/ PiperOrigin-RevId: 216761212 Change-Id: I244270a0a522298c48115719fa08dad55e34ade1 --- README.md | 2 +- runsc/boot/config.go | 7 +++---- runsc/main.go | 19 ++++++++----------- runsc/sandbox/sandbox.go | 6 +++--- runsc/specutils/specutils.go | 25 +++++++++++++++++++------ runsc/test/install.sh | 2 +- 6 files changed, 35 insertions(+), 26 deletions(-) (limited to 'runsc/specutils') diff --git a/README.md b/README.md index d85948ce5..a4fca1f62 100644 --- a/README.md +++ b/README.md @@ -297,7 +297,7 @@ Docker configuration (`/etc/docker/daemon.json`): "runsc": { "path": "/usr/local/bin/runsc", "runtimeArgs": [ - "--debug-log-dir=/tmp/runsc", + "--debug-log=/tmp/runsc/", "--debug", "--strace" ] diff --git a/runsc/boot/config.go b/runsc/boot/config.go index cd977c8a5..41af084b9 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -160,9 +160,8 @@ type Config struct { // LogFormat is the log format, "text" or "json". LogFormat string - // DebugLogDir is the directory to log debug information to, if not - // empty. - DebugLogDir string + // DebugLog is the path to log debug information to, if not empty. + DebugLog string // FileAccess indicates how the filesystem is accessed. FileAccess FileAccessType @@ -217,7 +216,7 @@ func (c *Config) ToFlags() []string { "--debug=" + strconv.FormatBool(c.Debug), "--log=" + c.LogFilename, "--log-format=" + c.LogFormat, - "--debug-log-dir=" + c.DebugLogDir, + "--debug-log=" + c.DebugLog, "--file-access=" + c.FileAccess.String(), "--overlay=" + strconv.FormatBool(c.Overlay), "--network=" + c.Network.String(), diff --git a/runsc/main.go b/runsc/main.go index 16d30f7a0..27aec1cd9 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -45,10 +45,10 @@ var ( // system that are not covered by the runtime spec. // Debugging flags. - debugLogDir = flag.String("debug-log-dir", "", "additional location for logs. It creates individual log files per command") - logPackets = flag.Bool("log-packets", false, "enable network packet logging") - logFD = flag.Int("log-fd", -1, "file descriptor to log to. If set, the 'log' flag is ignored.") - debugLogFD = flag.Int("debug-log-fd", -1, "file descriptor to write debug logs to. If set, the 'debug-log-dir' flag is ignored.") + debugLog = flag.String("debug-log", "", "additional location for logs. If it ends with '/', log files are created inside the directory with default names. The following variables are available: %TIMESTAMP%, %COMMAND%.") + logPackets = flag.Bool("log-packets", false, "enable network packet logging") + logFD = flag.Int("log-fd", -1, "file descriptor to log to. If set, the 'log' flag is ignored.") + debugLogFD = flag.Int("debug-log-fd", -1, "file descriptor to write debug logs to. If set, the 'debug-log-dir' flag is ignored.") // Debugging flags: strace related strace = flag.Bool("strace", false, "enable strace") @@ -131,7 +131,7 @@ func main() { Debug: *debug, LogFilename: *logFilename, LogFormat: *logFormat, - DebugLogDir: *debugLogDir, + DebugLog: *debugLog, FileAccess: fsAccess, Overlay: *overlay, Network: netType, @@ -195,13 +195,10 @@ func main() { } e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} - } else if *debugLogDir != "" { - if err := os.MkdirAll(*debugLogDir, 0775); err != nil { - cmd.Fatalf("error creating dir %q: %v", *debugLogDir, err) - } - f, err := specutils.DebugLogFile(*debugLogDir, subcommand) + } else if *debugLog != "" { + f, err := specutils.DebugLogFile(*debugLog, subcommand) if err != nil { - cmd.Fatalf("error opening debug log file in %q: %v", *debugLogDir, err) + cmd.Fatalf("error opening debug log file in %q: %v", *debugLog, err) } e = log.MultiEmitter{e, log.GoogleEmitter{&log.Writer{Next: f}}} } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 37a3efd09..a0de4a175 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -291,10 +291,10 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund cmd.Args = append(cmd.Args, "--log-fd="+strconv.Itoa(nextFD)) nextFD++ } - if conf.DebugLogDir != "" { - debugLogFile, err := specutils.DebugLogFile(conf.DebugLogDir, "boot") + if conf.DebugLog != "" { + debugLogFile, err := specutils.DebugLogFile(conf.DebugLog, "boot") if err != nil { - return fmt.Errorf("error opening debug log file in %q: %v", conf.DebugLogDir, err) + return fmt.Errorf("error opening debug log file in %q: %v", conf.DebugLog, err) } defer debugLogFile.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, debugLogFile) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index ac017ba2d..6b3e52021 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -351,12 +351,25 @@ func WaitForReady(pid int, timeout time.Duration, ready func() (bool, error)) er return backoff.Retry(op, b) } -// DebugLogFile opens a file in logDir based on the timestamp and subcommand -// for writing. -func DebugLogFile(logDir, subcommand string) (*os.File, error) { - // Format: /runsc.log.. - filename := fmt.Sprintf("runsc.log.%s.%s", time.Now().Format("20060102-150405.000000"), subcommand) - return os.OpenFile(filepath.Join(logDir, filename), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) +// DebugLogFile opens a log file using 'logPattern' as location. If 'logPattern' +// ends with '/', it's used as a directory with default file name. +// 'logPattern' can contain variables that are substitued: +// - %TIMESTAMP%: is replaced with a timestamp using the following format: +// +// - %COMMAND%: is replaced with 'command' +func DebugLogFile(logPattern, command string) (*os.File, error) { + if strings.HasSuffix(logPattern, "/") { + // Default format: /runsc.log.. + logPattern += "runsc.log.%TIMESTAMP%.%COMMAND%" + } + logPattern = strings.Replace(logPattern, "%TIMESTAMP%", time.Now().Format("20060102-150405.000000"), -1) + logPattern = strings.Replace(logPattern, "%COMMAND%", command, -1) + + dir := filepath.Dir(logPattern) + if err := os.MkdirAll(dir, 0775); err != nil { + return nil, fmt.Errorf("error creating dir %q: %v", dir, err) + } + return os.OpenFile(logPattern, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) } // Mount creates the mount point and calls Mount with the given flags. diff --git a/runsc/test/install.sh b/runsc/test/install.sh index c110d96f9..c239588d4 100755 --- a/runsc/test/install.sh +++ b/runsc/test/install.sh @@ -75,7 +75,7 @@ if [[ ${uninstall} == 0 ]]; then mkdir -p "${logdir}" sudo -n chmod a+wx "${logdir}" - declare -r args="--debug-log-dir "${logdir}" --debug --strace --log-packets" + declare -r args="--debug-log '${logdir}/' --debug --strace --log-packets" sudo -n "${dockercfg}" runtime-add "${runtime}" "${runsc}" ${args} sudo -n "${dockercfg}" runtime-add "${runtime}"-kvm "${runsc}" --platform=kvm ${args} sudo -n "${dockercfg}" runtime-add "${runtime}"-hostnet "${runsc}" --network=host ${args} -- cgit v1.2.3 From f3ffa4db525ea1a1d36307ea9593ed7b5e014ca7 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 18 Oct 2018 12:41:07 -0700 Subject: Resolve mount paths while setting up root fs mount It's hard to resolve symlinks inside the sandbox because rootfs and mounts may be read-only, forcing us to create mount points inside lower layer of an overlay, **before** the volumes are mounted. Since the destination must already be resolved outside the sandbox when creating mounts, take this opportunity to rewrite the spec with paths resolved. "runsc boot" will use the "resolved" spec to load mounts. In addition, symlink traversals were disabled while mounting containers inside the sandbox. It haven't been able to write a good test for it. So I'm relying on manual tests for now. PiperOrigin-RevId: 217749904 Change-Id: I7ac434d5befd230db1488446cda03300cc0751a9 --- runsc/boot/config.go | 3 - runsc/boot/fs.go | 143 +++++++++++++------------------------- runsc/cmd/create.go | 3 - runsc/cmd/run.go | 2 - runsc/container/container.go | 26 +++++-- runsc/container/container_test.go | 2 +- runsc/container/fs.go | 30 +++++--- runsc/sandbox/sandbox.go | 7 +- runsc/specutils/specutils.go | 23 ++++++ runsc/test/testutil/testutil.go | 1 - 10 files changed, 119 insertions(+), 121 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/config.go b/runsc/boot/config.go index 41af084b9..51d20d06d 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -192,9 +192,6 @@ type Config struct { // disabled. Pardon the double negation, but default to enabled is important. DisableSeccomp bool - // SpecFile is the file containing the OCI spec. - SpecFile string - // WatchdogAction sets what action the watchdog takes when triggered. WatchdogAction watchdog.Action diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 42e011beb..ea825e571 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -103,9 +103,14 @@ func createMountNamespace(userCtx context.Context, rootCtx context.Context, spec return nil, fmt.Errorf("failed to create root mount namespace: %v", err) } - if err := setMounts(rootCtx, conf, mns, fds, mounts); err != nil { - return nil, fmt.Errorf("failed to configure mounts: %v", err) + root := mns.Root() + defer root.DecRef() + for _, m := range mounts { + if err := mountSubmount(rootCtx, conf, mns, root, fds, m, mounts); err != nil { + return nil, fmt.Errorf("mount submount: %v", err) + } } + if !fds.empty() { return nil, fmt.Errorf("not all mount points were consumed, remaining: %v", fds) } @@ -184,17 +189,6 @@ func compileMounts(spec *specs.Spec) []specs.Mount { return mounts } -// setMounts iterates over mounts and mounts them in the specified -// mount namespace. -func setMounts(ctx context.Context, conf *Config, mns *fs.MountNamespace, fds *fdDispenser, mounts []specs.Mount) error { - for _, m := range mounts { - if err := mountSubmount(ctx, conf, mns, fds, m, mounts, m.Destination); err != nil { - return err - } - } - return nil -} - // createRootMount creates the root filesystem. func createRootMount(ctx context.Context, spec *specs.Spec, conf *Config, fds *fdDispenser, mounts []specs.Mount) (*fs.Inode, error) { // First construct the filesystem from the spec.Root. @@ -207,9 +201,9 @@ func createRootMount(ctx context.Context, spec *specs.Spec, conf *Config, fds *f fd := fds.remove() log.Infof("Mounting root over 9P, ioFD: %d", fd) - hostFS := mustFindFilesystem("9p") + p9FS := mustFindFilesystem("9p") opts := p9MountOptions(fd, conf.FileAccess) - rootInode, err = hostFS.Mount(ctx, rootDevice, mf, strings.Join(opts, ",")) + rootInode, err = p9FS.Mount(ctx, rootDevice, mf, strings.Join(opts, ",")) if err != nil { return nil, fmt.Errorf("failed to generate root mount point: %v", err) } @@ -294,7 +288,11 @@ func getMountNameAndOptions(conf *Config, m specs.Mount, fds *fdDispenser) (stri return fsName, opts, useOverlay, err } -func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, fds *fdDispenser, m specs.Mount, mounts []specs.Mount, dest string) error { +// mountSubmount mounts volumes inside the container's root. Because mounts may +// be readonly, a lower ramfs overlay is added to create the mount point dir. +// Another overlay is added with tmpfs on top if Config.Overlay is true. +// 'm.Destination' must be an absolute path with '..' and symlinks resolved. +func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, root *fs.Dirent, fds *fdDispenser, m specs.Mount, mounts []specs.Mount) error { // Map mount type to filesystem name, and parse out the options that we are // capable of dealing with. fsName, opts, useOverlay, err := getMountNameAndOptions(conf, m, fds) @@ -340,60 +338,16 @@ func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, fd } } - // Create destination in case it doesn't exist. This is required, in addition - // to 'addSubmountOverlay', in case there are symlinks to create directories - // in the right location, e.g. - // mount: /var/run/secrets, may be created in '/run/secrets' if - // '/var/run' => '/var'. - if err := mkdirAll(ctx, mns, dest); err != nil { - return err - } - - root := mns.Root() - defer root.DecRef() - dirent, err := mns.FindInode(ctx, root, nil, dest, linux.MaxSymlinkTraversals) + dirent, err := mns.FindInode(ctx, root, root, m.Destination, 0 /* maxTraversals */) if err != nil { - return fmt.Errorf("failed to find mount destination %q: %v", dest, err) + return fmt.Errorf("failed to find mount destination %q: %v", m.Destination, err) } defer dirent.DecRef() if err := mns.Mount(ctx, dirent, inode); err != nil { - return fmt.Errorf("failed to mount at destination %q: %v", dest, err) + return fmt.Errorf("failed to mount at destination %q: %v", m.Destination, err) } - log.Infof("Mounted %q to %q type %s", m.Source, dest, m.Type) - return nil -} - -func mkdirAll(ctx context.Context, mns *fs.MountNamespace, path string) error { - log.Infof("mkdirAll called with path %s", path) - root := mns.Root() - defer root.DecRef() - - // Starting at the root, walk the path. - parent := root - ps := strings.Split(filepath.Clean(path), string(filepath.Separator)) - for _, pathElem := range ps { - if pathElem == "" { - // This will be case for the first and last element, if the path - // begins or ends with '/'. Note that we always treat the path as - // absolute, regardless of what the first character contains. - continue - } - d, err := mns.FindInode(ctx, root, parent, pathElem, fs.DefaultTraversalLimit) - if err == syserror.ENOENT { - // If we encounter a path that does not exist, then - // create it. - if err := parent.CreateDirectory(ctx, root, pathElem, fs.FilePermsFromMode(0755)); err != nil { - return fmt.Errorf("failed to create directory %q: %v", pathElem, err) - } - if d, err = parent.Walk(ctx, root, pathElem); err != nil { - return fmt.Errorf("walk to %q failed: %v", pathElem, err) - } - } else if err != nil { - return fmt.Errorf("failed to find inode %q: %v", pathElem, err) - } - parent = d - } + log.Infof("Mounted %q to %q type %s", m.Source, m.Destination, m.Type) return nil } @@ -437,14 +391,6 @@ func parseAndFilterOptions(opts []string, allowedKeys ...string) ([]string, erro return out, nil } -func destinations(mounts []specs.Mount, extra ...string) []string { - var ds []string - for _, m := range mounts { - ds = append(ds, m.Destination) - } - return append(ds, extra...) -} - // mountDevice returns a device string based on the fs type and target // of the mount. func mountDevice(m specs.Mount) string { @@ -544,7 +490,8 @@ func mustFindFilesystem(name string) fs.Filesystem { func addSubmountOverlay(ctx context.Context, inode *fs.Inode, submounts []string) (*fs.Inode, error) { // There is no real filesystem backing this ramfs tree, so we pass in // "nil" here. - mountTree, err := ramfs.MakeDirectoryTree(ctx, fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{}), submounts) + msrc := fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{}) + mountTree, err := ramfs.MakeDirectoryTree(ctx, msrc, submounts) if err != nil { return nil, fmt.Errorf("error creating mount tree: %v", err) } @@ -608,12 +555,16 @@ func setupContainerFS(procArgs *kernel.CreateProcessArgs, spec *specs.Spec, conf // namespace. mns := k.RootMountNamespace() if mns == nil { + // Setup the root container. + // Create the virtual filesystem. mns, err := createMountNamespace(ctx, rootCtx, spec, conf, goferFDs) if err != nil { return fmt.Errorf("error creating mounts: %v", err) } k.SetRootMountNamespace(mns) + + // We're done with root container. return nil } @@ -627,42 +578,48 @@ func setupContainerFS(procArgs *kernel.CreateProcessArgs, spec *specs.Spec, conf return fmt.Errorf("error creating filesystem for container: %v", err) } - // Make directories for submounts within the container. - rootDir := mns.Root() - defer rootDir.DecRef() - containerRoot := filepath.Join(ChildContainersDir, cid) - mkdirAll(ctx, mns, containerRoot) + globalRoot := mns.Root() + defer globalRoot.DecRef() - // Mount the container's root filesystem to the newly created - // mount point. - containerRootDirent, err := mns.FindInode(ctx, rootDir, nil, containerRoot, linux.MaxSymlinkTraversals) + // Create mount point for the container's rootfs. + contDir, err := mns.FindInode(ctx, globalRoot, nil, ChildContainersDir, 0 /* TraversalLimit */) if err != nil { - return fmt.Errorf("failed to find mount destination: %q: %v", containerRoot, err) + return fmt.Errorf("couldn't find child container dir %q: %v", ChildContainersDir, err) } - if err := mns.Mount(ctx, containerRootDirent, rootInode); err != nil { - return fmt.Errorf("failed to mount at destination %q: %v", containerRoot, err) + if err := contDir.CreateDirectory(ctx, globalRoot, cid, fs.FilePermsFromMode(0755)); err != nil { + return fmt.Errorf("create directory %q: %v", cid, err) + } + containerRoot, err := contDir.Walk(ctx, globalRoot, cid) + if err != nil { + return fmt.Errorf("walk to %q failed: %v", cid, err) + } + defer containerRoot.DecRef() + + // Mount the container's root filesystem to the newly created mount point. + if err := mns.Mount(ctx, containerRoot, rootInode); err != nil { + return fmt.Errorf("mount container root: %v", err) } - containerRootDirent.DecRef() // We have to re-walk to the dirent to find the mounted // directory. The old dirent is invalid at this point. - containerRootDirent, err = mns.FindInode(ctx, rootDir, nil, containerRoot, linux.MaxSymlinkTraversals) + containerRoot, err = contDir.Walk(ctx, globalRoot, cid) if err != nil { - return fmt.Errorf("failed to find mount destination2: %q: %v", containerRoot, err) + return fmt.Errorf("find container mount point %q: %v", cid, err) } - log.Infof("Mounted child's root fs to %q", containerRoot) + + log.Infof("Mounted child's root fs to %q", filepath.Join(ChildContainersDir, cid)) // Mount all submounts. mounts := compileMounts(spec) for _, m := range mounts { - dest := filepath.Join(containerRoot, m.Destination) - if err := mountSubmount(rootCtx, conf, k.RootMountNamespace(), fds, m, mounts, dest); err != nil { + if err := mountSubmount(rootCtx, conf, k.RootMountNamespace(), containerRoot, fds, m, mounts); err != nil { + containerRoot.DecRef() return fmt.Errorf("error mounting filesystem for container: %v", err) } } // Set the procArgs root directory. - procArgs.Root = containerRootDirent + procArgs.Root = containerRoot return nil } @@ -686,7 +643,7 @@ func destroyContainerFS(ctx context.Context, cid string, k *kernel.Kernel) error mnsRoot := mns.Root() defer mnsRoot.DecRef() containerRoot := path.Join(ChildContainersDir, cid) - containerRootDirent, err := mns.FindInode(ctx, mnsRoot, nil, containerRoot, linux.MaxSymlinkTraversals) + containerRootDirent, err := mns.FindInode(ctx, mnsRoot, nil, containerRoot, 0 /* maxTraversals */) if err == syserror.ENOENT { // Container must have been destroyed already. That's fine. return nil @@ -720,7 +677,7 @@ func destroyContainerFS(ctx context.Context, cid string, k *kernel.Kernel) error // Get a reference to the parent directory and remove the root // container directory. - containersDirDirent, err := mns.FindInode(ctx, mnsRoot, nil, ChildContainersDir, linux.MaxSymlinkTraversals) + containersDirDirent, err := mns.FindInode(ctx, mnsRoot, nil, ChildContainersDir, 0 /* maxTraversals */) if err != nil { return fmt.Errorf("error finding containers directory %q: %v", ChildContainersDir, err) } diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index ecd76ee93..275a96f57 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -15,8 +15,6 @@ package cmd import ( - "path/filepath" - "context" "flag" "github.com/google/subcommands" @@ -93,7 +91,6 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} Fatalf("error reading spec: %v", err) } specutils.LogSpec(spec) - conf.SpecFile = filepath.Join(bundleDir, "config.json") // Create the container. A new sandbox will be created for the // container unless the metadata specifies that it should be run in an diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index 826e6e875..9a87cf240 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -15,7 +15,6 @@ package cmd import ( - "path/filepath" "syscall" "context" @@ -73,7 +72,6 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s Fatalf("error reading spec: %v", err) } specutils.LogSpec(spec) - conf.SpecFile = filepath.Join(bundleDir, "config.json") ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile, r.userLog) if err != nil { diff --git a/runsc/container/container.go b/runsc/container/container.go index 0ec4d03c1..f76bad1aa 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -271,6 +271,19 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // init container in the sandbox. if specutils.ShouldCreateSandbox(spec) { log.Debugf("Creating new sandbox for container %q", id) + + // Setup rootfs and mounts. It returns a new mount list with destination + // paths resolved. Since the spec for the root container is read from disk, + // Write the new spec to a new file that will be used by the sandbox. + cleanMounts, err := setupFS(spec, conf, bundleDir) + if err != nil { + return nil, fmt.Errorf("setup mounts: %v", err) + } + spec.Mounts = cleanMounts + if err := specutils.WriteCleanSpec(bundleDir, spec); err != nil { + return nil, fmt.Errorf("writing clean spec: %v", err) + } + ioFiles, err := c.createGoferProcess(spec, conf, bundleDir) if err != nil { return nil, err @@ -351,6 +364,15 @@ func (c *Container) Start(conf *boot.Config) error { return err } } else { + // Setup rootfs and mounts. It returns a new mount list with destination + // paths resolved. Replace the original spec with new mount list and start + // container. + cleanMounts, err := setupFS(c.Spec, conf, c.BundleDir) + if err != nil { + return fmt.Errorf("setup mounts: %v", err) + } + c.Spec.Mounts = cleanMounts + // Create the gofer process. ioFiles, err := c.createGoferProcess(c.Spec, conf, c.BundleDir) if err != nil { @@ -691,10 +713,6 @@ func (c *Container) waitForStopped() error { } func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]*os.File, error) { - if err := setupFS(spec, conf, bundleDir); err != nil { - return nil, fmt.Errorf("failed to setup mounts: %v", err) - } - // Start with the general config flags. args := conf.ToFlags() args = append(args, "gofer", "--bundle", bundleDir) diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index e2bb7d8ec..662591b3b 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -458,7 +458,7 @@ func TestAppExitStatus(t *testing.T) { defer os.RemoveAll(rootDir2) defer os.RemoveAll(bundleDir2) - ws, err = Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir2, "", "", "") + ws, err = Run(testutil.UniqueContainerID(), errSpec, conf, bundleDir2, "", "", "") if err != nil { t.Fatalf("error running container: %v", err) } diff --git a/runsc/container/fs.go b/runsc/container/fs.go index 59edd9488..2ed42fd93 100644 --- a/runsc/container/fs.go +++ b/runsc/container/fs.go @@ -73,9 +73,13 @@ var optionsMap = map[string]mapping{ // This allows the gofer serving the containers to be chroot under this // directory to create an extra layer to security in case the gofer gets // compromised. -func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { +// Returns list of mounts equivalent to 'spec.Mounts' with all destination paths +// cleaned and with symlinks resolved. +func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]specs.Mount, error) { + rv := make([]specs.Mount, 0, len(spec.Mounts)) for _, m := range spec.Mounts { if m.Type != "bind" || !specutils.IsSupportedDevMount(m) { + rv = append(rv, m) continue } @@ -83,39 +87,47 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { // container. dst, err := resolveSymlinks(spec.Root.Path, m.Destination) if err != nil { - return fmt.Errorf("failed to resolve symlinks: %v", err) + return nil, fmt.Errorf("failed to resolve symlinks: %v", err) } flags := optionsToFlags(m.Options) flags |= syscall.MS_BIND log.Infof("Mounting src: %q, dst: %q, flags: %#x", m.Source, dst, flags) if err := specutils.Mount(m.Source, dst, m.Type, flags); err != nil { - return fmt.Errorf("failed to mount %v: %v", m, err) + return nil, fmt.Errorf("failed to mount %v: %v", m, err) } // Make the mount a slave, so that for recursive bind mount, umount won't // propagate to the source. flags = syscall.MS_SLAVE | syscall.MS_REC if err := syscall.Mount("", dst, "", uintptr(flags), ""); err != nil { - return fmt.Errorf("failed to rslave mount dst: %q, flags: %#x, err: %v", dst, flags, err) + return nil, fmt.Errorf("failed to rslave mount dst: %q, flags: %#x, err: %v", dst, flags, err) } + + cpy := m + relDst, err := filepath.Rel(spec.Root.Path, dst) + if err != nil { + panic(fmt.Sprintf("%q could not be made relative to %q: %v", dst, spec.Root.Path, err)) + } + cpy.Destination = filepath.Join("/", relDst) + rv = append(rv, cpy) } // If root is read only, check if it needs to be remounted as readonly. if spec.Root.Readonly { isMountPoint, readonly, err := mountInfo(spec.Root.Path) if err != nil { - return err + return nil, err } if readonly { - return nil + return rv, nil } if !isMountPoint { // Readonly root is not a mount point nor read-only. Can't do much other // than just logging a warning. The gofer will prevent files to be open // in write mode. log.Warningf("Mount where root is located is not read-only and cannot be changed: %q", spec.Root.Path) - return nil + return rv, nil } // If root is a mount point but not read-only, we can change mount options @@ -124,10 +136,10 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error { flags := uintptr(syscall.MS_BIND | syscall.MS_REMOUNT | syscall.MS_RDONLY | syscall.MS_REC) src := spec.Root.Path if err := syscall.Mount(src, src, "bind", flags, ""); err != nil { - return fmt.Errorf("failed to remount root as read-only with source: %q, target: %q, flags: %#x, err: %v", spec.Root.Path, spec.Root.Path, flags, err) + return nil, fmt.Errorf("failed to remount root as read-only with source: %q, target: %q, flags: %#x, err: %v", spec.Root.Path, spec.Root.Path, flags, err) } } - return nil + return rv, nil } // mountInfo returns whether the path is a mount point and whether the mount diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 6dc8cf7f0..923a52f7f 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -321,12 +321,9 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund nextFD++ // Open the spec file to donate to the sandbox. - if conf.SpecFile == "" { - return fmt.Errorf("conf.SpecFile must be set") - } - specFile, err := os.Open(conf.SpecFile) + specFile, err := specutils.OpenCleanSpec(bundleDir) if err != nil { - return fmt.Errorf("error opening spec file %q: %v", conf.SpecFile, err) + return fmt.Errorf("opening spec file: %v", err) } defer specFile.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, specFile) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 6b3e52021..b29802fde 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -170,6 +170,29 @@ func ReadSpecFromFile(bundleDir string, specFile *os.File) (*specs.Spec, error) return &spec, nil } +// OpenCleanSpec opens spec file that has destination mount paths resolved to +// their absolute location. +func OpenCleanSpec(bundleDir string) (*os.File, error) { + f, err := os.Open(filepath.Join(bundleDir, "config.clean.json")) + if err != nil { + return nil, err + } + if _, err := f.Seek(0, os.SEEK_SET); err != nil { + f.Close() + return nil, fmt.Errorf("error seeking to beginning of file %q: %v", f.Name(), err) + } + return f, nil +} + +// WriteCleanSpec writes a spec file that has destination mount paths resolved. +func WriteCleanSpec(bundleDir string, spec *specs.Spec) error { + bytes, err := json.Marshal(spec) + if err != nil { + return err + } + return ioutil.WriteFile(filepath.Join(bundleDir, "config.clean.json"), bytes, 0755) +} + // Capabilities takes in spec and returns a TaskCapabilities corresponding to // the spec. func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) { diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index b4664995c..4d7ac3bc9 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -179,7 +179,6 @@ func SetupContainerInRoot(rootDir string, spec *specs.Spec, conf *boot.Config) ( } conf.RootDir = rootDir - conf.SpecFile = filepath.Join(bundleDir, "config.json") return bundleDir, nil } -- cgit v1.2.3 From 8fce67af24945f82378b4c2731cca1788936d074 Mon Sep 17 00:00:00 2001 From: Ian Gudger Date: Fri, 19 Oct 2018 16:34:09 -0700 Subject: Use correct company name in copyright header PiperOrigin-RevId: 217951017 Change-Id: Ie08bf6987f98467d07457bcf35b5f1ff6e43c035 --- kokoro/run_build.sh | 2 +- kokoro/run_tests.sh | 2 +- pkg/abi/abi.go | 2 +- pkg/abi/abi_linux.go | 2 +- pkg/abi/flag.go | 2 +- pkg/abi/linux/aio.go | 2 +- pkg/abi/linux/ashmem.go | 2 +- pkg/abi/linux/binder.go | 2 +- pkg/abi/linux/bpf.go | 2 +- pkg/abi/linux/capability.go | 2 +- pkg/abi/linux/dev.go | 2 +- pkg/abi/linux/elf.go | 2 +- pkg/abi/linux/errors.go | 2 +- pkg/abi/linux/eventfd.go | 2 +- pkg/abi/linux/exec.go | 2 +- pkg/abi/linux/fcntl.go | 2 +- pkg/abi/linux/file.go | 2 +- pkg/abi/linux/fs.go | 2 +- pkg/abi/linux/futex.go | 2 +- pkg/abi/linux/inotify.go | 2 +- pkg/abi/linux/ioctl.go | 2 +- pkg/abi/linux/ip.go | 2 +- pkg/abi/linux/ipc.go | 2 +- pkg/abi/linux/limits.go | 2 +- pkg/abi/linux/linux.go | 2 +- pkg/abi/linux/mm.go | 2 +- pkg/abi/linux/netdevice.go | 2 +- pkg/abi/linux/netlink.go | 2 +- pkg/abi/linux/netlink_route.go | 2 +- pkg/abi/linux/poll.go | 2 +- pkg/abi/linux/prctl.go | 2 +- pkg/abi/linux/ptrace.go | 2 +- pkg/abi/linux/rusage.go | 2 +- pkg/abi/linux/sched.go | 2 +- pkg/abi/linux/seccomp.go | 2 +- pkg/abi/linux/sem.go | 2 +- pkg/abi/linux/shm.go | 2 +- pkg/abi/linux/signal.go | 2 +- pkg/abi/linux/socket.go | 2 +- pkg/abi/linux/time.go | 2 +- pkg/abi/linux/timer.go | 2 +- pkg/abi/linux/tty.go | 2 +- pkg/abi/linux/uio.go | 2 +- pkg/abi/linux/utsname.go | 2 +- pkg/amutex/amutex.go | 2 +- pkg/amutex/amutex_test.go | 2 +- pkg/atomicbitops/atomic_bitops.go | 2 +- pkg/atomicbitops/atomic_bitops_amd64.s | 2 +- pkg/atomicbitops/atomic_bitops_common.go | 2 +- pkg/atomicbitops/atomic_bitops_test.go | 2 +- pkg/binary/binary.go | 2 +- pkg/binary/binary_test.go | 2 +- pkg/bits/bits.go | 2 +- pkg/bits/bits_template.go | 2 +- pkg/bits/uint64_arch_amd64.go | 2 +- pkg/bits/uint64_arch_amd64_asm.s | 2 +- pkg/bits/uint64_arch_generic.go | 2 +- pkg/bits/uint64_test.go | 2 +- pkg/bpf/bpf.go | 2 +- pkg/bpf/decoder.go | 2 +- pkg/bpf/decoder_test.go | 2 +- pkg/bpf/input_bytes.go | 2 +- pkg/bpf/interpreter.go | 2 +- pkg/bpf/interpreter_test.go | 2 +- pkg/bpf/program_builder.go | 2 +- pkg/bpf/program_builder_test.go | 2 +- pkg/compressio/compressio.go | 2 +- pkg/compressio/compressio_test.go | 2 +- pkg/control/client/client.go | 2 +- pkg/control/server/server.go | 2 +- pkg/cpuid/cpu_amd64.s | 2 +- pkg/cpuid/cpuid.go | 2 +- pkg/cpuid/cpuid_parse_test.go | 2 +- pkg/cpuid/cpuid_test.go | 2 +- pkg/dhcp/client.go | 2 +- pkg/dhcp/dhcp.go | 2 +- pkg/dhcp/dhcp_string.go | 2 +- pkg/dhcp/dhcp_test.go | 2 +- pkg/dhcp/server.go | 2 +- pkg/eventchannel/event.go | 2 +- pkg/eventchannel/event.proto | 2 +- pkg/fd/fd.go | 2 +- pkg/fd/fd_test.go | 2 +- pkg/gate/gate.go | 2 +- pkg/gate/gate_test.go | 2 +- pkg/ilist/list.go | 2 +- pkg/ilist/list_test.go | 2 +- pkg/linewriter/linewriter.go | 2 +- pkg/linewriter/linewriter_test.go | 2 +- pkg/log/glog.go | 2 +- pkg/log/glog_unsafe.go | 2 +- pkg/log/json.go | 2 +- pkg/log/json_test.go | 2 +- pkg/log/log.go | 2 +- pkg/log/log_test.go | 2 +- pkg/metric/metric.go | 2 +- pkg/metric/metric.proto | 2 +- pkg/metric/metric_test.go | 2 +- pkg/p9/buffer.go | 2 +- pkg/p9/client.go | 2 +- pkg/p9/client_file.go | 2 +- pkg/p9/client_test.go | 2 +- pkg/p9/file.go | 2 +- pkg/p9/handlers.go | 2 +- pkg/p9/local_server/local_server.go | 2 +- pkg/p9/messages.go | 2 +- pkg/p9/messages_test.go | 2 +- pkg/p9/p9.go | 2 +- pkg/p9/p9_test.go | 2 +- pkg/p9/p9test/client_test.go | 2 +- pkg/p9/p9test/mocks.go | 2 +- pkg/p9/pool.go | 2 +- pkg/p9/pool_test.go | 2 +- pkg/p9/server.go | 2 +- pkg/p9/transport.go | 2 +- pkg/p9/transport_test.go | 2 +- pkg/p9/version.go | 2 +- pkg/p9/version_test.go | 2 +- pkg/rand/rand.go | 2 +- pkg/rand/rand_linux.go | 2 +- pkg/refs/refcounter.go | 2 +- pkg/refs/refcounter_state.go | 2 +- pkg/refs/refcounter_test.go | 2 +- pkg/seccomp/seccomp.go | 2 +- pkg/seccomp/seccomp_rules.go | 2 +- pkg/seccomp/seccomp_test.go | 2 +- pkg/seccomp/seccomp_test_victim.go | 2 +- pkg/seccomp/seccomp_unsafe.go | 2 +- pkg/secio/full_reader.go | 2 +- pkg/secio/secio.go | 2 +- pkg/secio/secio_test.go | 2 +- pkg/segment/range.go | 2 +- pkg/segment/set.go | 2 +- pkg/segment/set_state.go | 2 +- pkg/segment/test/segment_test.go | 2 +- pkg/segment/test/set_functions.go | 2 +- pkg/sentry/arch/aligned.go | 2 +- pkg/sentry/arch/arch.go | 2 +- pkg/sentry/arch/arch_amd64.go | 2 +- pkg/sentry/arch/arch_amd64.s | 2 +- pkg/sentry/arch/arch_state_x86.go | 2 +- pkg/sentry/arch/arch_x86.go | 2 +- pkg/sentry/arch/auxv.go | 2 +- pkg/sentry/arch/registers.proto | 2 +- pkg/sentry/arch/signal_act.go | 2 +- pkg/sentry/arch/signal_amd64.go | 2 +- pkg/sentry/arch/signal_info.go | 2 +- pkg/sentry/arch/signal_stack.go | 2 +- pkg/sentry/arch/stack.go | 2 +- pkg/sentry/arch/syscalls_amd64.go | 2 +- pkg/sentry/context/context.go | 2 +- pkg/sentry/context/contexttest/contexttest.go | 2 +- pkg/sentry/control/control.go | 2 +- pkg/sentry/control/proc.go | 2 +- pkg/sentry/control/proc_test.go | 2 +- pkg/sentry/control/state.go | 2 +- pkg/sentry/device/device.go | 2 +- pkg/sentry/device/device_test.go | 2 +- pkg/sentry/fs/anon/anon.go | 2 +- pkg/sentry/fs/anon/device.go | 2 +- pkg/sentry/fs/ashmem/area.go | 2 +- pkg/sentry/fs/ashmem/device.go | 2 +- pkg/sentry/fs/ashmem/pin_board.go | 2 +- pkg/sentry/fs/ashmem/pin_board_test.go | 2 +- pkg/sentry/fs/attr.go | 2 +- pkg/sentry/fs/binder/binder.go | 2 +- pkg/sentry/fs/context.go | 2 +- pkg/sentry/fs/copy_up.go | 2 +- pkg/sentry/fs/copy_up_test.go | 2 +- pkg/sentry/fs/dentry.go | 2 +- pkg/sentry/fs/dev/dev.go | 2 +- pkg/sentry/fs/dev/device.go | 2 +- pkg/sentry/fs/dev/fs.go | 2 +- pkg/sentry/fs/dev/full.go | 2 +- pkg/sentry/fs/dev/null.go | 2 +- pkg/sentry/fs/dev/random.go | 2 +- pkg/sentry/fs/dirent.go | 2 +- pkg/sentry/fs/dirent_cache.go | 2 +- pkg/sentry/fs/dirent_cache_test.go | 2 +- pkg/sentry/fs/dirent_refs_test.go | 2 +- pkg/sentry/fs/dirent_state.go | 2 +- pkg/sentry/fs/fdpipe/pipe.go | 2 +- pkg/sentry/fs/fdpipe/pipe_opener.go | 2 +- pkg/sentry/fs/fdpipe/pipe_opener_test.go | 2 +- pkg/sentry/fs/fdpipe/pipe_state.go | 2 +- pkg/sentry/fs/fdpipe/pipe_test.go | 2 +- pkg/sentry/fs/file.go | 2 +- pkg/sentry/fs/file_operations.go | 2 +- pkg/sentry/fs/file_overlay.go | 2 +- pkg/sentry/fs/file_overlay_test.go | 2 +- pkg/sentry/fs/file_state.go | 2 +- pkg/sentry/fs/file_test.go | 2 +- pkg/sentry/fs/filesystems.go | 2 +- pkg/sentry/fs/filetest/filetest.go | 2 +- pkg/sentry/fs/flags.go | 2 +- pkg/sentry/fs/fs.go | 2 +- pkg/sentry/fs/fsutil/dirty_set.go | 2 +- pkg/sentry/fs/fsutil/dirty_set_test.go | 2 +- pkg/sentry/fs/fsutil/file.go | 2 +- pkg/sentry/fs/fsutil/file_range_set.go | 2 +- pkg/sentry/fs/fsutil/frame_ref_set.go | 2 +- pkg/sentry/fs/fsutil/fsutil.go | 2 +- pkg/sentry/fs/fsutil/handle.go | 2 +- pkg/sentry/fs/fsutil/handle_test.go | 2 +- pkg/sentry/fs/fsutil/host_file_mapper.go | 2 +- pkg/sentry/fs/fsutil/host_file_mapper_state.go | 2 +- pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go | 2 +- pkg/sentry/fs/fsutil/inode.go | 2 +- pkg/sentry/fs/fsutil/inode_cached.go | 2 +- pkg/sentry/fs/fsutil/inode_cached_test.go | 2 +- pkg/sentry/fs/gofer/attr.go | 2 +- pkg/sentry/fs/gofer/cache_policy.go | 2 +- pkg/sentry/fs/gofer/context_file.go | 2 +- pkg/sentry/fs/gofer/device.go | 2 +- pkg/sentry/fs/gofer/file.go | 2 +- pkg/sentry/fs/gofer/file_state.go | 2 +- pkg/sentry/fs/gofer/fs.go | 2 +- pkg/sentry/fs/gofer/gofer_test.go | 2 +- pkg/sentry/fs/gofer/handles.go | 2 +- pkg/sentry/fs/gofer/inode.go | 2 +- pkg/sentry/fs/gofer/inode_state.go | 2 +- pkg/sentry/fs/gofer/path.go | 2 +- pkg/sentry/fs/gofer/session.go | 2 +- pkg/sentry/fs/gofer/session_state.go | 2 +- pkg/sentry/fs/gofer/socket.go | 2 +- pkg/sentry/fs/gofer/util.go | 2 +- pkg/sentry/fs/host/control.go | 2 +- pkg/sentry/fs/host/descriptor.go | 2 +- pkg/sentry/fs/host/descriptor_state.go | 2 +- pkg/sentry/fs/host/descriptor_test.go | 2 +- pkg/sentry/fs/host/device.go | 2 +- pkg/sentry/fs/host/file.go | 2 +- pkg/sentry/fs/host/fs.go | 2 +- pkg/sentry/fs/host/fs_test.go | 2 +- pkg/sentry/fs/host/inode.go | 2 +- pkg/sentry/fs/host/inode_state.go | 2 +- pkg/sentry/fs/host/inode_test.go | 2 +- pkg/sentry/fs/host/ioctl_unsafe.go | 2 +- pkg/sentry/fs/host/socket.go | 2 +- pkg/sentry/fs/host/socket_iovec.go | 2 +- pkg/sentry/fs/host/socket_state.go | 2 +- pkg/sentry/fs/host/socket_test.go | 2 +- pkg/sentry/fs/host/socket_unsafe.go | 2 +- pkg/sentry/fs/host/tty.go | 2 +- pkg/sentry/fs/host/util.go | 2 +- pkg/sentry/fs/host/util_unsafe.go | 2 +- pkg/sentry/fs/host/wait_test.go | 2 +- pkg/sentry/fs/inode.go | 2 +- pkg/sentry/fs/inode_inotify.go | 2 +- pkg/sentry/fs/inode_operations.go | 2 +- pkg/sentry/fs/inode_overlay.go | 2 +- pkg/sentry/fs/inode_overlay_test.go | 2 +- pkg/sentry/fs/inotify.go | 2 +- pkg/sentry/fs/inotify_event.go | 2 +- pkg/sentry/fs/inotify_watch.go | 2 +- pkg/sentry/fs/lock/lock.go | 2 +- pkg/sentry/fs/lock/lock_range_test.go | 2 +- pkg/sentry/fs/lock/lock_set_functions.go | 2 +- pkg/sentry/fs/lock/lock_test.go | 2 +- pkg/sentry/fs/mock.go | 2 +- pkg/sentry/fs/mount.go | 2 +- pkg/sentry/fs/mount_overlay.go | 2 +- pkg/sentry/fs/mount_state.go | 2 +- pkg/sentry/fs/mount_test.go | 2 +- pkg/sentry/fs/mounts.go | 2 +- pkg/sentry/fs/mounts_test.go | 2 +- pkg/sentry/fs/offset.go | 2 +- pkg/sentry/fs/overlay.go | 2 +- pkg/sentry/fs/path.go | 2 +- pkg/sentry/fs/path_test.go | 2 +- pkg/sentry/fs/proc/cpuinfo.go | 2 +- pkg/sentry/fs/proc/device/device.go | 2 +- pkg/sentry/fs/proc/exec_args.go | 2 +- pkg/sentry/fs/proc/fds.go | 2 +- pkg/sentry/fs/proc/file.go | 2 +- pkg/sentry/fs/proc/filesystems.go | 2 +- pkg/sentry/fs/proc/fs.go | 2 +- pkg/sentry/fs/proc/loadavg.go | 2 +- pkg/sentry/fs/proc/meminfo.go | 2 +- pkg/sentry/fs/proc/mounts.go | 2 +- pkg/sentry/fs/proc/net.go | 2 +- pkg/sentry/fs/proc/net_test.go | 2 +- pkg/sentry/fs/proc/proc.go | 2 +- pkg/sentry/fs/proc/rpcinet_proc.go | 2 +- pkg/sentry/fs/proc/seqfile/seqfile.go | 2 +- pkg/sentry/fs/proc/seqfile/seqfile_test.go | 2 +- pkg/sentry/fs/proc/stat.go | 2 +- pkg/sentry/fs/proc/sys.go | 2 +- pkg/sentry/fs/proc/sys_net.go | 2 +- pkg/sentry/fs/proc/sys_net_test.go | 2 +- pkg/sentry/fs/proc/task.go | 2 +- pkg/sentry/fs/proc/uid_gid_map.go | 2 +- pkg/sentry/fs/proc/uptime.go | 2 +- pkg/sentry/fs/proc/version.go | 2 +- pkg/sentry/fs/ramfs/dir.go | 2 +- pkg/sentry/fs/ramfs/file.go | 2 +- pkg/sentry/fs/ramfs/ramfs.go | 2 +- pkg/sentry/fs/ramfs/socket.go | 2 +- pkg/sentry/fs/ramfs/symlink.go | 2 +- pkg/sentry/fs/ramfs/test/test.go | 2 +- pkg/sentry/fs/ramfs/tree.go | 2 +- pkg/sentry/fs/ramfs/tree_test.go | 2 +- pkg/sentry/fs/restore.go | 2 +- pkg/sentry/fs/save.go | 2 +- pkg/sentry/fs/seek.go | 2 +- pkg/sentry/fs/sync.go | 2 +- pkg/sentry/fs/sys/device.go | 2 +- pkg/sentry/fs/sys/devices.go | 2 +- pkg/sentry/fs/sys/fs.go | 2 +- pkg/sentry/fs/sys/sys.go | 2 +- pkg/sentry/fs/timerfd/timerfd.go | 2 +- pkg/sentry/fs/tmpfs/device.go | 2 +- pkg/sentry/fs/tmpfs/file_regular.go | 2 +- pkg/sentry/fs/tmpfs/file_test.go | 2 +- pkg/sentry/fs/tmpfs/fs.go | 2 +- pkg/sentry/fs/tmpfs/inode_file.go | 2 +- pkg/sentry/fs/tmpfs/tmpfs.go | 2 +- pkg/sentry/fs/tty/dir.go | 2 +- pkg/sentry/fs/tty/fs.go | 2 +- pkg/sentry/fs/tty/inode.go | 2 +- pkg/sentry/fs/tty/line_discipline.go | 2 +- pkg/sentry/fs/tty/master.go | 2 +- pkg/sentry/fs/tty/queue.go | 2 +- pkg/sentry/fs/tty/slave.go | 2 +- pkg/sentry/fs/tty/terminal.go | 2 +- pkg/sentry/fs/tty/tty_test.go | 2 +- pkg/sentry/hostcpu/getcpu_amd64.s | 2 +- pkg/sentry/hostcpu/hostcpu.go | 2 +- pkg/sentry/hostcpu/hostcpu_test.go | 2 +- pkg/sentry/inet/context.go | 2 +- pkg/sentry/inet/inet.go | 2 +- pkg/sentry/inet/test_stack.go | 2 +- pkg/sentry/kernel/abstract_socket_namespace.go | 2 +- pkg/sentry/kernel/auth/auth.go | 2 +- pkg/sentry/kernel/auth/capability_set.go | 2 +- pkg/sentry/kernel/auth/context.go | 2 +- pkg/sentry/kernel/auth/credentials.go | 2 +- pkg/sentry/kernel/auth/id.go | 2 +- pkg/sentry/kernel/auth/id_map.go | 2 +- pkg/sentry/kernel/auth/id_map_functions.go | 2 +- pkg/sentry/kernel/auth/user_namespace.go | 2 +- pkg/sentry/kernel/context.go | 2 +- pkg/sentry/kernel/epoll/epoll.go | 2 +- pkg/sentry/kernel/epoll/epoll_state.go | 2 +- pkg/sentry/kernel/epoll/epoll_test.go | 2 +- pkg/sentry/kernel/eventfd/eventfd.go | 2 +- pkg/sentry/kernel/eventfd/eventfd_test.go | 2 +- pkg/sentry/kernel/fasync/fasync.go | 2 +- pkg/sentry/kernel/fd_map.go | 2 +- pkg/sentry/kernel/fd_map_test.go | 2 +- pkg/sentry/kernel/fs_context.go | 2 +- pkg/sentry/kernel/futex/futex.go | 2 +- pkg/sentry/kernel/futex/futex_test.go | 2 +- pkg/sentry/kernel/ipc_namespace.go | 2 +- pkg/sentry/kernel/kdefs/kdefs.go | 2 +- pkg/sentry/kernel/kernel.go | 2 +- pkg/sentry/kernel/kernel_state.go | 2 +- pkg/sentry/kernel/memevent/memory_events.go | 2 +- pkg/sentry/kernel/memevent/memory_events.proto | 2 +- pkg/sentry/kernel/pending_signals.go | 2 +- pkg/sentry/kernel/pending_signals_state.go | 2 +- pkg/sentry/kernel/pipe/buffers.go | 2 +- pkg/sentry/kernel/pipe/device.go | 2 +- pkg/sentry/kernel/pipe/node.go | 2 +- pkg/sentry/kernel/pipe/node_test.go | 2 +- pkg/sentry/kernel/pipe/pipe.go | 2 +- pkg/sentry/kernel/pipe/pipe_test.go | 2 +- pkg/sentry/kernel/pipe/reader.go | 2 +- pkg/sentry/kernel/pipe/reader_writer.go | 2 +- pkg/sentry/kernel/pipe/writer.go | 2 +- pkg/sentry/kernel/posixtimer.go | 2 +- pkg/sentry/kernel/ptrace.go | 2 +- pkg/sentry/kernel/rseq.go | 2 +- pkg/sentry/kernel/sched/cpuset.go | 2 +- pkg/sentry/kernel/sched/cpuset_test.go | 2 +- pkg/sentry/kernel/sched/sched.go | 2 +- pkg/sentry/kernel/seccomp.go | 2 +- pkg/sentry/kernel/semaphore/semaphore.go | 2 +- pkg/sentry/kernel/semaphore/semaphore_test.go | 2 +- pkg/sentry/kernel/sessions.go | 2 +- pkg/sentry/kernel/shm/device.go | 2 +- pkg/sentry/kernel/shm/shm.go | 2 +- pkg/sentry/kernel/signal.go | 2 +- pkg/sentry/kernel/signal_handlers.go | 2 +- pkg/sentry/kernel/syscalls.go | 2 +- pkg/sentry/kernel/syscalls_state.go | 2 +- pkg/sentry/kernel/syslog.go | 2 +- pkg/sentry/kernel/table_test.go | 2 +- pkg/sentry/kernel/task.go | 2 +- pkg/sentry/kernel/task_acct.go | 2 +- pkg/sentry/kernel/task_block.go | 2 +- pkg/sentry/kernel/task_clone.go | 2 +- pkg/sentry/kernel/task_context.go | 2 +- pkg/sentry/kernel/task_exec.go | 2 +- pkg/sentry/kernel/task_exit.go | 2 +- pkg/sentry/kernel/task_futex.go | 2 +- pkg/sentry/kernel/task_identity.go | 2 +- pkg/sentry/kernel/task_log.go | 2 +- pkg/sentry/kernel/task_net.go | 2 +- pkg/sentry/kernel/task_run.go | 2 +- pkg/sentry/kernel/task_sched.go | 2 +- pkg/sentry/kernel/task_signals.go | 2 +- pkg/sentry/kernel/task_start.go | 2 +- pkg/sentry/kernel/task_stop.go | 2 +- pkg/sentry/kernel/task_syscall.go | 2 +- pkg/sentry/kernel/task_test.go | 2 +- pkg/sentry/kernel/task_usermem.go | 2 +- pkg/sentry/kernel/thread_group.go | 2 +- pkg/sentry/kernel/threads.go | 2 +- pkg/sentry/kernel/time/context.go | 2 +- pkg/sentry/kernel/time/time.go | 2 +- pkg/sentry/kernel/timekeeper.go | 2 +- pkg/sentry/kernel/timekeeper_state.go | 2 +- pkg/sentry/kernel/timekeeper_test.go | 2 +- pkg/sentry/kernel/uts_namespace.go | 2 +- pkg/sentry/kernel/vdso.go | 2 +- pkg/sentry/kernel/version.go | 2 +- pkg/sentry/limits/context.go | 2 +- pkg/sentry/limits/limits.go | 2 +- pkg/sentry/limits/limits_test.go | 2 +- pkg/sentry/limits/linux.go | 2 +- pkg/sentry/loader/elf.go | 2 +- pkg/sentry/loader/interpreter.go | 2 +- pkg/sentry/loader/loader.go | 2 +- pkg/sentry/loader/vdso.go | 2 +- pkg/sentry/loader/vdso_state.go | 2 +- pkg/sentry/memmap/mapping_set.go | 2 +- pkg/sentry/memmap/mapping_set_test.go | 2 +- pkg/sentry/memmap/memmap.go | 2 +- pkg/sentry/memutil/memutil.go | 2 +- pkg/sentry/memutil/memutil_unsafe.go | 2 +- pkg/sentry/mm/address_space.go | 2 +- pkg/sentry/mm/aio_context.go | 2 +- pkg/sentry/mm/aio_context_state.go | 2 +- pkg/sentry/mm/debug.go | 2 +- pkg/sentry/mm/io.go | 2 +- pkg/sentry/mm/lifecycle.go | 2 +- pkg/sentry/mm/metadata.go | 2 +- pkg/sentry/mm/mm.go | 2 +- pkg/sentry/mm/mm_test.go | 2 +- pkg/sentry/mm/pma.go | 2 +- pkg/sentry/mm/proc_pid_maps.go | 2 +- pkg/sentry/mm/save_restore.go | 2 +- pkg/sentry/mm/shm.go | 2 +- pkg/sentry/mm/special_mappable.go | 2 +- pkg/sentry/mm/syscalls.go | 2 +- pkg/sentry/mm/vma.go | 2 +- pkg/sentry/platform/context.go | 2 +- pkg/sentry/platform/filemem/filemem.go | 2 +- pkg/sentry/platform/filemem/filemem_state.go | 2 +- pkg/sentry/platform/filemem/filemem_test.go | 2 +- pkg/sentry/platform/filemem/filemem_unsafe.go | 2 +- pkg/sentry/platform/interrupt/interrupt.go | 2 +- pkg/sentry/platform/interrupt/interrupt_test.go | 2 +- pkg/sentry/platform/kvm/address_space.go | 2 +- pkg/sentry/platform/kvm/allocator.go | 2 +- pkg/sentry/platform/kvm/bluepill.go | 2 +- pkg/sentry/platform/kvm/bluepill_amd64.go | 2 +- pkg/sentry/platform/kvm/bluepill_amd64.s | 2 +- pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go | 2 +- pkg/sentry/platform/kvm/bluepill_fault.go | 2 +- pkg/sentry/platform/kvm/bluepill_unsafe.go | 2 +- pkg/sentry/platform/kvm/context.go | 2 +- pkg/sentry/platform/kvm/host_map.go | 2 +- pkg/sentry/platform/kvm/kvm.go | 2 +- pkg/sentry/platform/kvm/kvm_amd64.go | 2 +- pkg/sentry/platform/kvm/kvm_amd64_unsafe.go | 2 +- pkg/sentry/platform/kvm/kvm_const.go | 2 +- pkg/sentry/platform/kvm/kvm_test.go | 2 +- pkg/sentry/platform/kvm/machine.go | 2 +- pkg/sentry/platform/kvm/machine_amd64.go | 2 +- pkg/sentry/platform/kvm/machine_amd64_unsafe.go | 2 +- pkg/sentry/platform/kvm/machine_unsafe.go | 2 +- pkg/sentry/platform/kvm/physical_map.go | 2 +- pkg/sentry/platform/kvm/testutil/testutil.go | 2 +- pkg/sentry/platform/kvm/testutil/testutil_amd64.go | 2 +- pkg/sentry/platform/kvm/testutil/testutil_amd64.s | 2 +- pkg/sentry/platform/kvm/virtual_map.go | 2 +- pkg/sentry/platform/kvm/virtual_map_test.go | 2 +- pkg/sentry/platform/mmap_min_addr.go | 2 +- pkg/sentry/platform/platform.go | 2 +- pkg/sentry/platform/procid/procid.go | 2 +- pkg/sentry/platform/procid/procid_amd64.s | 2 +- pkg/sentry/platform/procid/procid_net_test.go | 2 +- pkg/sentry/platform/procid/procid_test.go | 2 +- pkg/sentry/platform/ptrace/ptrace.go | 2 +- pkg/sentry/platform/ptrace/ptrace_unsafe.go | 2 +- pkg/sentry/platform/ptrace/stub_amd64.s | 2 +- pkg/sentry/platform/ptrace/stub_unsafe.go | 2 +- pkg/sentry/platform/ptrace/subprocess.go | 2 +- pkg/sentry/platform/ptrace/subprocess_amd64.go | 2 +- pkg/sentry/platform/ptrace/subprocess_linux.go | 2 +- pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go | 2 +- pkg/sentry/platform/ptrace/subprocess_unsafe.go | 2 +- pkg/sentry/platform/ring0/defs.go | 2 +- pkg/sentry/platform/ring0/defs_amd64.go | 2 +- pkg/sentry/platform/ring0/entry_amd64.go | 2 +- pkg/sentry/platform/ring0/entry_amd64.s | 2 +- pkg/sentry/platform/ring0/gen_offsets/main.go | 2 +- pkg/sentry/platform/ring0/kernel.go | 2 +- pkg/sentry/platform/ring0/kernel_amd64.go | 2 +- pkg/sentry/platform/ring0/kernel_unsafe.go | 2 +- pkg/sentry/platform/ring0/lib_amd64.go | 2 +- pkg/sentry/platform/ring0/lib_amd64.s | 2 +- pkg/sentry/platform/ring0/offsets_amd64.go | 2 +- pkg/sentry/platform/ring0/pagetables/allocator.go | 2 +- pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_test.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_x86.go | 2 +- pkg/sentry/platform/ring0/pagetables/pcids_x86.go | 2 +- pkg/sentry/platform/ring0/pagetables/walker_amd64.go | 2 +- pkg/sentry/platform/ring0/ring0.go | 2 +- pkg/sentry/platform/ring0/x86.go | 2 +- pkg/sentry/platform/safecopy/atomic_amd64.s | 2 +- pkg/sentry/platform/safecopy/memclr_amd64.s | 2 +- pkg/sentry/platform/safecopy/memcpy_amd64.s | 2 +- pkg/sentry/platform/safecopy/safecopy.go | 2 +- pkg/sentry/platform/safecopy/safecopy_test.go | 2 +- pkg/sentry/platform/safecopy/safecopy_unsafe.go | 2 +- pkg/sentry/platform/safecopy/sighandler_amd64.s | 2 +- pkg/sentry/safemem/block_unsafe.go | 2 +- pkg/sentry/safemem/io.go | 2 +- pkg/sentry/safemem/io_test.go | 2 +- pkg/sentry/safemem/safemem.go | 2 +- pkg/sentry/safemem/seq_test.go | 2 +- pkg/sentry/safemem/seq_unsafe.go | 2 +- pkg/sentry/sighandling/sighandling.go | 2 +- pkg/sentry/sighandling/sighandling_unsafe.go | 2 +- pkg/sentry/socket/control/control.go | 2 +- pkg/sentry/socket/epsocket/device.go | 2 +- pkg/sentry/socket/epsocket/epsocket.go | 2 +- pkg/sentry/socket/epsocket/provider.go | 2 +- pkg/sentry/socket/epsocket/save_restore.go | 2 +- pkg/sentry/socket/epsocket/stack.go | 2 +- pkg/sentry/socket/hostinet/device.go | 2 +- pkg/sentry/socket/hostinet/hostinet.go | 2 +- pkg/sentry/socket/hostinet/save_restore.go | 2 +- pkg/sentry/socket/hostinet/socket.go | 2 +- pkg/sentry/socket/hostinet/socket_unsafe.go | 2 +- pkg/sentry/socket/hostinet/stack.go | 2 +- pkg/sentry/socket/netlink/message.go | 2 +- pkg/sentry/socket/netlink/port/port.go | 2 +- pkg/sentry/socket/netlink/port/port_test.go | 2 +- pkg/sentry/socket/netlink/provider.go | 2 +- pkg/sentry/socket/netlink/route/protocol.go | 2 +- pkg/sentry/socket/netlink/socket.go | 2 +- pkg/sentry/socket/rpcinet/conn/conn.go | 2 +- pkg/sentry/socket/rpcinet/device.go | 2 +- pkg/sentry/socket/rpcinet/notifier/notifier.go | 2 +- pkg/sentry/socket/rpcinet/rpcinet.go | 2 +- pkg/sentry/socket/rpcinet/socket.go | 2 +- pkg/sentry/socket/rpcinet/stack.go | 2 +- pkg/sentry/socket/rpcinet/stack_unsafe.go | 2 +- pkg/sentry/socket/socket.go | 2 +- pkg/sentry/socket/unix/device.go | 2 +- pkg/sentry/socket/unix/io.go | 2 +- pkg/sentry/socket/unix/transport/connectioned.go | 2 +- pkg/sentry/socket/unix/transport/connectioned_state.go | 2 +- pkg/sentry/socket/unix/transport/connectionless.go | 2 +- pkg/sentry/socket/unix/transport/queue.go | 2 +- pkg/sentry/socket/unix/transport/unix.go | 2 +- pkg/sentry/socket/unix/unix.go | 2 +- pkg/sentry/state/state.go | 2 +- pkg/sentry/state/state_metadata.go | 2 +- pkg/sentry/state/state_unsafe.go | 2 +- pkg/sentry/strace/clone.go | 2 +- pkg/sentry/strace/futex.go | 2 +- pkg/sentry/strace/linux64.go | 2 +- pkg/sentry/strace/open.go | 2 +- pkg/sentry/strace/ptrace.go | 2 +- pkg/sentry/strace/socket.go | 2 +- pkg/sentry/strace/strace.go | 2 +- pkg/sentry/strace/strace.proto | 2 +- pkg/sentry/strace/syscalls.go | 2 +- pkg/sentry/syscalls/epoll.go | 2 +- pkg/sentry/syscalls/linux/error.go | 2 +- pkg/sentry/syscalls/linux/flags.go | 2 +- pkg/sentry/syscalls/linux/linux64.go | 2 +- pkg/sentry/syscalls/linux/sigset.go | 2 +- pkg/sentry/syscalls/linux/sys_aio.go | 2 +- pkg/sentry/syscalls/linux/sys_capability.go | 2 +- pkg/sentry/syscalls/linux/sys_epoll.go | 2 +- pkg/sentry/syscalls/linux/sys_eventfd.go | 2 +- pkg/sentry/syscalls/linux/sys_file.go | 2 +- pkg/sentry/syscalls/linux/sys_futex.go | 2 +- pkg/sentry/syscalls/linux/sys_getdents.go | 2 +- pkg/sentry/syscalls/linux/sys_identity.go | 2 +- pkg/sentry/syscalls/linux/sys_inotify.go | 2 +- pkg/sentry/syscalls/linux/sys_lseek.go | 2 +- pkg/sentry/syscalls/linux/sys_mmap.go | 2 +- pkg/sentry/syscalls/linux/sys_mount.go | 2 +- pkg/sentry/syscalls/linux/sys_pipe.go | 2 +- pkg/sentry/syscalls/linux/sys_poll.go | 2 +- pkg/sentry/syscalls/linux/sys_prctl.go | 2 +- pkg/sentry/syscalls/linux/sys_random.go | 2 +- pkg/sentry/syscalls/linux/sys_read.go | 2 +- pkg/sentry/syscalls/linux/sys_rlimit.go | 2 +- pkg/sentry/syscalls/linux/sys_rusage.go | 2 +- pkg/sentry/syscalls/linux/sys_sched.go | 2 +- pkg/sentry/syscalls/linux/sys_seccomp.go | 2 +- pkg/sentry/syscalls/linux/sys_sem.go | 2 +- pkg/sentry/syscalls/linux/sys_shm.go | 2 +- pkg/sentry/syscalls/linux/sys_signal.go | 2 +- pkg/sentry/syscalls/linux/sys_socket.go | 2 +- pkg/sentry/syscalls/linux/sys_stat.go | 2 +- pkg/sentry/syscalls/linux/sys_sync.go | 2 +- pkg/sentry/syscalls/linux/sys_sysinfo.go | 2 +- pkg/sentry/syscalls/linux/sys_syslog.go | 2 +- pkg/sentry/syscalls/linux/sys_thread.go | 2 +- pkg/sentry/syscalls/linux/sys_time.go | 2 +- pkg/sentry/syscalls/linux/sys_timer.go | 2 +- pkg/sentry/syscalls/linux/sys_timerfd.go | 2 +- pkg/sentry/syscalls/linux/sys_tls.go | 2 +- pkg/sentry/syscalls/linux/sys_utsname.go | 2 +- pkg/sentry/syscalls/linux/sys_write.go | 2 +- pkg/sentry/syscalls/linux/timespec.go | 2 +- pkg/sentry/syscalls/polling.go | 2 +- pkg/sentry/syscalls/syscalls.go | 2 +- pkg/sentry/syscalls/unimplemented_syscall.proto | 2 +- pkg/sentry/time/calibrated_clock.go | 2 +- pkg/sentry/time/calibrated_clock_test.go | 2 +- pkg/sentry/time/clock_id.go | 2 +- pkg/sentry/time/clocks.go | 2 +- pkg/sentry/time/muldiv_amd64.s | 2 +- pkg/sentry/time/parameters.go | 2 +- pkg/sentry/time/parameters_test.go | 2 +- pkg/sentry/time/sampler.go | 2 +- pkg/sentry/time/sampler_test.go | 2 +- pkg/sentry/time/sampler_unsafe.go | 2 +- pkg/sentry/time/tsc_amd64.s | 2 +- pkg/sentry/uniqueid/context.go | 2 +- pkg/sentry/usage/cpu.go | 2 +- pkg/sentry/usage/io.go | 2 +- pkg/sentry/usage/memory.go | 2 +- pkg/sentry/usage/memory_unsafe.go | 2 +- pkg/sentry/usage/usage.go | 2 +- pkg/sentry/usermem/access_type.go | 2 +- pkg/sentry/usermem/addr.go | 2 +- pkg/sentry/usermem/addr_range_seq_test.go | 2 +- pkg/sentry/usermem/addr_range_seq_unsafe.go | 2 +- pkg/sentry/usermem/bytes_io.go | 2 +- pkg/sentry/usermem/bytes_io_unsafe.go | 2 +- pkg/sentry/usermem/usermem.go | 2 +- pkg/sentry/usermem/usermem_test.go | 2 +- pkg/sentry/usermem/usermem_x86.go | 2 +- pkg/sentry/watchdog/watchdog.go | 2 +- pkg/sleep/commit_amd64.s | 2 +- pkg/sleep/commit_asm.go | 2 +- pkg/sleep/commit_noasm.go | 2 +- pkg/sleep/empty.s | 2 +- pkg/sleep/sleep_test.go | 2 +- pkg/sleep/sleep_unsafe.go | 2 +- pkg/state/decode.go | 2 +- pkg/state/encode.go | 2 +- pkg/state/encode_unsafe.go | 2 +- pkg/state/map.go | 2 +- pkg/state/object.proto | 2 +- pkg/state/printer.go | 2 +- pkg/state/state.go | 2 +- pkg/state/state_test.go | 2 +- pkg/state/statefile/statefile.go | 2 +- pkg/state/statefile/statefile_test.go | 2 +- pkg/state/stats.go | 2 +- pkg/sync/atomicptr_unsafe.go | 2 +- pkg/sync/atomicptrtest/atomicptr_test.go | 2 +- pkg/sync/memmove_unsafe.go | 2 +- pkg/sync/norace_unsafe.go | 2 +- pkg/sync/race_unsafe.go | 2 +- pkg/sync/seqatomic_unsafe.go | 2 +- pkg/sync/seqatomictest/seqatomic_test.go | 2 +- pkg/sync/seqcount.go | 2 +- pkg/sync/seqcount_test.go | 2 +- pkg/sync/sync.go | 2 +- pkg/syserr/host_linux.go | 2 +- pkg/syserr/netstack.go | 2 +- pkg/syserr/syserr.go | 2 +- pkg/syserror/syserror.go | 2 +- pkg/syserror/syserror_test.go | 2 +- pkg/tcpip/adapters/gonet/gonet.go | 2 +- pkg/tcpip/adapters/gonet/gonet_test.go | 2 +- pkg/tcpip/buffer/prependable.go | 2 +- pkg/tcpip/buffer/view.go | 2 +- pkg/tcpip/buffer/view_test.go | 2 +- pkg/tcpip/checker/checker.go | 2 +- pkg/tcpip/header/arp.go | 2 +- pkg/tcpip/header/checksum.go | 2 +- pkg/tcpip/header/eth.go | 2 +- pkg/tcpip/header/gue.go | 2 +- pkg/tcpip/header/icmpv4.go | 2 +- pkg/tcpip/header/icmpv6.go | 2 +- pkg/tcpip/header/interfaces.go | 2 +- pkg/tcpip/header/ipv4.go | 2 +- pkg/tcpip/header/ipv6.go | 2 +- pkg/tcpip/header/ipv6_fragment.go | 2 +- pkg/tcpip/header/ipversion_test.go | 2 +- pkg/tcpip/header/tcp.go | 2 +- pkg/tcpip/header/tcp_test.go | 2 +- pkg/tcpip/header/udp.go | 2 +- pkg/tcpip/link/channel/channel.go | 2 +- pkg/tcpip/link/fdbased/endpoint.go | 2 +- pkg/tcpip/link/fdbased/endpoint_test.go | 2 +- pkg/tcpip/link/loopback/loopback.go | 2 +- pkg/tcpip/link/rawfile/blockingpoll_amd64.s | 2 +- pkg/tcpip/link/rawfile/blockingpoll_unsafe.go | 2 +- pkg/tcpip/link/rawfile/blockingpoll_unsafe_amd64.go | 2 +- pkg/tcpip/link/rawfile/errors.go | 2 +- pkg/tcpip/link/rawfile/rawfile_unsafe.go | 2 +- pkg/tcpip/link/sharedmem/pipe/pipe.go | 2 +- pkg/tcpip/link/sharedmem/pipe/pipe_test.go | 2 +- pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go | 2 +- pkg/tcpip/link/sharedmem/pipe/rx.go | 2 +- pkg/tcpip/link/sharedmem/pipe/tx.go | 2 +- pkg/tcpip/link/sharedmem/queue/queue_test.go | 2 +- pkg/tcpip/link/sharedmem/queue/rx.go | 2 +- pkg/tcpip/link/sharedmem/queue/tx.go | 2 +- pkg/tcpip/link/sharedmem/rx.go | 2 +- pkg/tcpip/link/sharedmem/sharedmem.go | 2 +- pkg/tcpip/link/sharedmem/sharedmem_test.go | 2 +- pkg/tcpip/link/sharedmem/sharedmem_unsafe.go | 2 +- pkg/tcpip/link/sharedmem/tx.go | 2 +- pkg/tcpip/link/sniffer/pcap.go | 2 +- pkg/tcpip/link/sniffer/sniffer.go | 2 +- pkg/tcpip/link/tun/tun_unsafe.go | 2 +- pkg/tcpip/link/waitable/waitable.go | 2 +- pkg/tcpip/link/waitable/waitable_test.go | 2 +- pkg/tcpip/network/arp/arp.go | 2 +- pkg/tcpip/network/arp/arp_test.go | 2 +- pkg/tcpip/network/fragmentation/frag_heap.go | 2 +- pkg/tcpip/network/fragmentation/frag_heap_test.go | 2 +- pkg/tcpip/network/fragmentation/fragmentation.go | 2 +- pkg/tcpip/network/fragmentation/fragmentation_test.go | 2 +- pkg/tcpip/network/fragmentation/reassembler.go | 2 +- pkg/tcpip/network/fragmentation/reassembler_test.go | 2 +- pkg/tcpip/network/hash/hash.go | 2 +- pkg/tcpip/network/ip_test.go | 2 +- pkg/tcpip/network/ipv4/icmp.go | 2 +- pkg/tcpip/network/ipv4/ipv4.go | 2 +- pkg/tcpip/network/ipv4/ipv4_test.go | 2 +- pkg/tcpip/network/ipv6/icmp.go | 2 +- pkg/tcpip/network/ipv6/icmp_test.go | 2 +- pkg/tcpip/network/ipv6/ipv6.go | 2 +- pkg/tcpip/ports/ports.go | 2 +- pkg/tcpip/ports/ports_test.go | 2 +- pkg/tcpip/sample/tun_tcp_connect/main.go | 2 +- pkg/tcpip/sample/tun_tcp_echo/main.go | 2 +- pkg/tcpip/seqnum/seqnum.go | 2 +- pkg/tcpip/stack/linkaddrcache.go | 2 +- pkg/tcpip/stack/linkaddrcache_test.go | 2 +- pkg/tcpip/stack/nic.go | 2 +- pkg/tcpip/stack/registration.go | 2 +- pkg/tcpip/stack/route.go | 2 +- pkg/tcpip/stack/stack.go | 2 +- pkg/tcpip/stack/stack_global_state.go | 2 +- pkg/tcpip/stack/stack_test.go | 2 +- pkg/tcpip/stack/transport_demuxer.go | 2 +- pkg/tcpip/stack/transport_test.go | 2 +- pkg/tcpip/tcpip.go | 2 +- pkg/tcpip/tcpip_test.go | 2 +- pkg/tcpip/time.s | 2 +- pkg/tcpip/time_unsafe.go | 2 +- pkg/tcpip/transport/ping/endpoint.go | 2 +- pkg/tcpip/transport/ping/endpoint_state.go | 2 +- pkg/tcpip/transport/ping/protocol.go | 2 +- pkg/tcpip/transport/tcp/accept.go | 2 +- pkg/tcpip/transport/tcp/connect.go | 2 +- pkg/tcpip/transport/tcp/cubic.go | 2 +- pkg/tcpip/transport/tcp/dual_stack_test.go | 2 +- pkg/tcpip/transport/tcp/endpoint.go | 2 +- pkg/tcpip/transport/tcp/endpoint_state.go | 2 +- pkg/tcpip/transport/tcp/forwarder.go | 2 +- pkg/tcpip/transport/tcp/protocol.go | 2 +- pkg/tcpip/transport/tcp/rcv.go | 2 +- pkg/tcpip/transport/tcp/reno.go | 2 +- pkg/tcpip/transport/tcp/sack.go | 2 +- pkg/tcpip/transport/tcp/segment.go | 2 +- pkg/tcpip/transport/tcp/segment_heap.go | 2 +- pkg/tcpip/transport/tcp/segment_queue.go | 2 +- pkg/tcpip/transport/tcp/segment_state.go | 2 +- pkg/tcpip/transport/tcp/snd.go | 2 +- pkg/tcpip/transport/tcp/snd_state.go | 2 +- pkg/tcpip/transport/tcp/tcp_sack_test.go | 2 +- pkg/tcpip/transport/tcp/tcp_test.go | 2 +- pkg/tcpip/transport/tcp/tcp_timestamp_test.go | 2 +- pkg/tcpip/transport/tcp/testing/context/context.go | 2 +- pkg/tcpip/transport/tcp/timer.go | 2 +- pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go | 2 +- pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go | 2 +- pkg/tcpip/transport/udp/endpoint.go | 2 +- pkg/tcpip/transport/udp/endpoint_state.go | 2 +- pkg/tcpip/transport/udp/protocol.go | 2 +- pkg/tcpip/transport/udp/udp_test.go | 2 +- pkg/tmutex/tmutex.go | 2 +- pkg/tmutex/tmutex_test.go | 2 +- pkg/unet/unet.go | 2 +- pkg/unet/unet_test.go | 2 +- pkg/unet/unet_unsafe.go | 2 +- pkg/urpc/urpc.go | 2 +- pkg/urpc/urpc_test.go | 2 +- pkg/waiter/fdnotifier/fdnotifier.go | 2 +- pkg/waiter/fdnotifier/poll_unsafe.go | 2 +- pkg/waiter/waiter.go | 2 +- pkg/waiter/waiter_test.go | 2 +- runsc/boot/compat.go | 2 +- runsc/boot/config.go | 2 +- runsc/boot/controller.go | 2 +- runsc/boot/debug.go | 2 +- runsc/boot/events.go | 2 +- runsc/boot/fds.go | 2 +- runsc/boot/filter/config.go | 2 +- runsc/boot/filter/extra_filters.go | 2 +- runsc/boot/filter/extra_filters_msan.go | 2 +- runsc/boot/filter/extra_filters_race.go | 2 +- runsc/boot/filter/filter.go | 2 +- runsc/boot/fs.go | 2 +- runsc/boot/limits.go | 2 +- runsc/boot/loader.go | 2 +- runsc/boot/loader_test.go | 2 +- runsc/boot/network.go | 2 +- runsc/boot/strace.go | 2 +- runsc/cgroup/cgroup.go | 2 +- runsc/cgroup/cgroup_test.go | 2 +- runsc/cmd/boot.go | 2 +- runsc/cmd/capability.go | 2 +- runsc/cmd/capability_test.go | 2 +- runsc/cmd/checkpoint.go | 2 +- runsc/cmd/cmd.go | 2 +- runsc/cmd/create.go | 2 +- runsc/cmd/debug.go | 2 +- runsc/cmd/delete.go | 2 +- runsc/cmd/delete_test.go | 2 +- runsc/cmd/events.go | 2 +- runsc/cmd/exec.go | 2 +- runsc/cmd/exec_test.go | 2 +- runsc/cmd/gofer.go | 2 +- runsc/cmd/kill.go | 2 +- runsc/cmd/list.go | 2 +- runsc/cmd/path.go | 2 +- runsc/cmd/pause.go | 2 +- runsc/cmd/ps.go | 2 +- runsc/cmd/restore.go | 2 +- runsc/cmd/resume.go | 2 +- runsc/cmd/run.go | 2 +- runsc/cmd/spec.go | 2 +- runsc/cmd/start.go | 2 +- runsc/cmd/state.go | 2 +- runsc/cmd/wait.go | 2 +- runsc/console/console.go | 2 +- runsc/container/console_test.go | 2 +- runsc/container/container.go | 2 +- runsc/container/container_test.go | 2 +- runsc/container/fs.go | 2 +- runsc/container/fs_test.go | 2 +- runsc/container/hook.go | 2 +- runsc/container/multi_container_test.go | 2 +- runsc/container/status.go | 2 +- runsc/container/test_app.go | 2 +- runsc/fsgofer/filter/config.go | 2 +- runsc/fsgofer/filter/extra_filters.go | 2 +- runsc/fsgofer/filter/extra_filters_msan.go | 2 +- runsc/fsgofer/filter/extra_filters_race.go | 2 +- runsc/fsgofer/filter/filter.go | 2 +- runsc/fsgofer/fsgofer.go | 2 +- runsc/fsgofer/fsgofer_test.go | 2 +- runsc/fsgofer/fsgofer_unsafe.go | 2 +- runsc/main.go | 2 +- runsc/sandbox/chroot.go | 2 +- runsc/sandbox/network.go | 2 +- runsc/sandbox/sandbox.go | 2 +- runsc/specutils/namespace.go | 2 +- runsc/specutils/specutils.go | 2 +- runsc/specutils/specutils_test.go | 2 +- runsc/test/image/image.go | 2 +- runsc/test/image/image_test.go | 2 +- runsc/test/image/mysql.sql | 2 +- runsc/test/image/ruby.rb | 2 +- runsc/test/image/ruby.sh | 2 +- runsc/test/install.sh | 2 +- runsc/test/integration/exec_test.go | 2 +- runsc/test/integration/integration.go | 2 +- runsc/test/integration/integration_test.go | 2 +- runsc/test/root/cgroup_test.go | 2 +- runsc/test/root/chroot_test.go | 2 +- runsc/test/root/root.go | 2 +- runsc/test/testutil/docker.go | 2 +- runsc/test/testutil/testutil.go | 2 +- runsc/test/testutil/testutil_race.go | 2 +- runsc/tools/dockercfg/dockercfg.go | 2 +- tools/go_generics/generics.go | 2 +- tools/go_generics/generics_tests/all_stmts/input.go | 2 +- tools/go_generics/generics_tests/all_stmts/output/output.go | 2 +- tools/go_generics/generics_tests/all_types/input.go | 2 +- tools/go_generics/generics_tests/all_types/lib/lib.go | 2 +- tools/go_generics/generics_tests/all_types/output/output.go | 2 +- tools/go_generics/generics_tests/consts/input.go | 2 +- tools/go_generics/generics_tests/consts/output/output.go | 2 +- tools/go_generics/generics_tests/imports/input.go | 2 +- tools/go_generics/generics_tests/imports/output/output.go | 2 +- tools/go_generics/generics_tests/remove_typedef/input.go | 2 +- tools/go_generics/generics_tests/remove_typedef/output/output.go | 2 +- tools/go_generics/generics_tests/simple/input.go | 2 +- tools/go_generics/generics_tests/simple/output/output.go | 2 +- tools/go_generics/globals/globals_visitor.go | 2 +- tools/go_generics/globals/scope.go | 2 +- tools/go_generics/go_generics_unittest.sh | 2 +- tools/go_generics/imports.go | 2 +- tools/go_generics/merge.go | 2 +- tools/go_generics/remove.go | 2 +- tools/go_generics/rules_tests/template.go | 2 +- tools/go_generics/rules_tests/template_test.go | 2 +- tools/go_stateify/main.go | 2 +- tools/workspace_status.sh | 2 +- vdso/barrier.h | 2 +- vdso/check_vdso.py | 2 +- vdso/compiler.h | 2 +- vdso/cycle_clock.h | 2 +- vdso/seqlock.h | 2 +- vdso/syscalls.h | 2 +- vdso/vdso.cc | 2 +- vdso/vdso_time.cc | 2 +- vdso/vdso_time.h | 2 +- 923 files changed, 923 insertions(+), 923 deletions(-) (limited to 'runsc/specutils') diff --git a/kokoro/run_build.sh b/kokoro/run_build.sh index f2b719f52..89e24b037 100755 --- a/kokoro/run_build.sh +++ b/kokoro/run_build.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/kokoro/run_tests.sh b/kokoro/run_tests.sh index 3f8841cee..0a0d73d29 100755 --- a/kokoro/run_tests.sh +++ b/kokoro/run_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pkg/abi/abi.go b/pkg/abi/abi.go index a53c2747b..7770f0405 100644 --- a/pkg/abi/abi.go +++ b/pkg/abi/abi.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/abi_linux.go b/pkg/abi/abi_linux.go index dd5d67b51..9d9f361a4 100644 --- a/pkg/abi/abi_linux.go +++ b/pkg/abi/abi_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/flag.go b/pkg/abi/flag.go index 0391ccf37..0698e410f 100644 --- a/pkg/abi/flag.go +++ b/pkg/abi/flag.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/aio.go b/pkg/abi/linux/aio.go index 9c39ca2ef..1b7ca714a 100644 --- a/pkg/abi/linux/aio.go +++ b/pkg/abi/linux/aio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/ashmem.go b/pkg/abi/linux/ashmem.go index 7fbfd2e68..ced1e44d4 100644 --- a/pkg/abi/linux/ashmem.go +++ b/pkg/abi/linux/ashmem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/binder.go b/pkg/abi/linux/binder.go index b228898f9..522dc6f53 100644 --- a/pkg/abi/linux/binder.go +++ b/pkg/abi/linux/binder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/bpf.go b/pkg/abi/linux/bpf.go index 80e5b1af1..d9cd09948 100644 --- a/pkg/abi/linux/bpf.go +++ b/pkg/abi/linux/bpf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/capability.go b/pkg/abi/linux/capability.go index b470ce0a5..7d96f013e 100644 --- a/pkg/abi/linux/capability.go +++ b/pkg/abi/linux/capability.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/dev.go b/pkg/abi/linux/dev.go index ea5b16b7b..5b1199aac 100644 --- a/pkg/abi/linux/dev.go +++ b/pkg/abi/linux/dev.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/elf.go b/pkg/abi/linux/elf.go index 76c13b677..928067c04 100644 --- a/pkg/abi/linux/elf.go +++ b/pkg/abi/linux/elf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/errors.go b/pkg/abi/linux/errors.go index b5ddb2b2f..01e4095b8 100644 --- a/pkg/abi/linux/errors.go +++ b/pkg/abi/linux/errors.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/eventfd.go b/pkg/abi/linux/eventfd.go index bc0fb44d2..5614f5cf1 100644 --- a/pkg/abi/linux/eventfd.go +++ b/pkg/abi/linux/eventfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/exec.go b/pkg/abi/linux/exec.go index 4d81eca54..a07c29243 100644 --- a/pkg/abi/linux/exec.go +++ b/pkg/abi/linux/exec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/fcntl.go b/pkg/abi/linux/fcntl.go index 2a5ad6ed7..c8558933a 100644 --- a/pkg/abi/linux/fcntl.go +++ b/pkg/abi/linux/fcntl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/file.go b/pkg/abi/linux/file.go index 9bf229a57..72e5c6f83 100644 --- a/pkg/abi/linux/file.go +++ b/pkg/abi/linux/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/fs.go b/pkg/abi/linux/fs.go index 32a0812b4..7817bfb52 100644 --- a/pkg/abi/linux/fs.go +++ b/pkg/abi/linux/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/futex.go b/pkg/abi/linux/futex.go index f63f5200c..5dff01fba 100644 --- a/pkg/abi/linux/futex.go +++ b/pkg/abi/linux/futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/inotify.go b/pkg/abi/linux/inotify.go index 072a2d146..79c5d3593 100644 --- a/pkg/abi/linux/inotify.go +++ b/pkg/abi/linux/inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/ioctl.go b/pkg/abi/linux/ioctl.go index afd9ee82b..9afc3d1ef 100644 --- a/pkg/abi/linux/ioctl.go +++ b/pkg/abi/linux/ioctl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/ip.go b/pkg/abi/linux/ip.go index 6b68999ab..fcec16965 100644 --- a/pkg/abi/linux/ip.go +++ b/pkg/abi/linux/ip.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/ipc.go b/pkg/abi/linux/ipc.go index 81e9904dd..10681768b 100644 --- a/pkg/abi/linux/ipc.go +++ b/pkg/abi/linux/ipc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/limits.go b/pkg/abi/linux/limits.go index e1f0932ec..b2e51b9bd 100644 --- a/pkg/abi/linux/limits.go +++ b/pkg/abi/linux/limits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/linux.go b/pkg/abi/linux/linux.go index de2af80dc..d365f693d 100644 --- a/pkg/abi/linux/linux.go +++ b/pkg/abi/linux/linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/mm.go b/pkg/abi/linux/mm.go index b48e1d18a..3fcdf8235 100644 --- a/pkg/abi/linux/mm.go +++ b/pkg/abi/linux/mm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/netdevice.go b/pkg/abi/linux/netdevice.go index 88654a1b3..e3b6b1e40 100644 --- a/pkg/abi/linux/netdevice.go +++ b/pkg/abi/linux/netdevice.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/netlink.go b/pkg/abi/linux/netlink.go index e823ffa7e..10ceb5bf2 100644 --- a/pkg/abi/linux/netlink.go +++ b/pkg/abi/linux/netlink.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/netlink_route.go b/pkg/abi/linux/netlink_route.go index a5d778748..4200b6506 100644 --- a/pkg/abi/linux/netlink_route.go +++ b/pkg/abi/linux/netlink_route.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/poll.go b/pkg/abi/linux/poll.go index f373cfca1..9f0b15d1c 100644 --- a/pkg/abi/linux/poll.go +++ b/pkg/abi/linux/poll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/prctl.go b/pkg/abi/linux/prctl.go index 074ec03f0..e152c4c27 100644 --- a/pkg/abi/linux/prctl.go +++ b/pkg/abi/linux/prctl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/ptrace.go b/pkg/abi/linux/ptrace.go index ba48d4d6d..7db4f5464 100644 --- a/pkg/abi/linux/ptrace.go +++ b/pkg/abi/linux/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/rusage.go b/pkg/abi/linux/rusage.go index a4a89abda..7fea4b589 100644 --- a/pkg/abi/linux/rusage.go +++ b/pkg/abi/linux/rusage.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/sched.go b/pkg/abi/linux/sched.go index 05fda1604..ef96a3801 100644 --- a/pkg/abi/linux/sched.go +++ b/pkg/abi/linux/sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/seccomp.go b/pkg/abi/linux/seccomp.go index a8de9d3d0..9963ceeba 100644 --- a/pkg/abi/linux/seccomp.go +++ b/pkg/abi/linux/seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/sem.go b/pkg/abi/linux/sem.go index 3495f5cd0..d1a0bdb32 100644 --- a/pkg/abi/linux/sem.go +++ b/pkg/abi/linux/sem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/shm.go b/pkg/abi/linux/shm.go index f50b3c2e2..82a80e609 100644 --- a/pkg/abi/linux/shm.go +++ b/pkg/abi/linux/shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/signal.go b/pkg/abi/linux/signal.go index b2c7230c4..bf9bce6ed 100644 --- a/pkg/abi/linux/signal.go +++ b/pkg/abi/linux/signal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/socket.go b/pkg/abi/linux/socket.go index 19b5fa212..af0761a3b 100644 --- a/pkg/abi/linux/socket.go +++ b/pkg/abi/linux/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/time.go b/pkg/abi/linux/time.go index 4569f4208..bbd21e726 100644 --- a/pkg/abi/linux/time.go +++ b/pkg/abi/linux/time.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/timer.go b/pkg/abi/linux/timer.go index 6c4675c35..a6f420bdb 100644 --- a/pkg/abi/linux/timer.go +++ b/pkg/abi/linux/timer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/tty.go b/pkg/abi/linux/tty.go index f63dc52aa..e6f7c5b2a 100644 --- a/pkg/abi/linux/tty.go +++ b/pkg/abi/linux/tty.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/uio.go b/pkg/abi/linux/uio.go index 93c972774..7e00d9959 100644 --- a/pkg/abi/linux/uio.go +++ b/pkg/abi/linux/uio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/abi/linux/utsname.go b/pkg/abi/linux/utsname.go index 7d33d20de..f80ed7d4a 100644 --- a/pkg/abi/linux/utsname.go +++ b/pkg/abi/linux/utsname.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/amutex/amutex.go b/pkg/amutex/amutex.go index 1cb73359a..26b674435 100644 --- a/pkg/amutex/amutex.go +++ b/pkg/amutex/amutex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/amutex/amutex_test.go b/pkg/amutex/amutex_test.go index 876e47b19..104e0dab1 100644 --- a/pkg/amutex/amutex_test.go +++ b/pkg/amutex/amutex_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/atomicbitops/atomic_bitops.go b/pkg/atomicbitops/atomic_bitops.go index 6635ea0d2..9a57f9599 100644 --- a/pkg/atomicbitops/atomic_bitops.go +++ b/pkg/atomicbitops/atomic_bitops.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/atomicbitops/atomic_bitops_amd64.s b/pkg/atomicbitops/atomic_bitops_amd64.s index 542452bec..b37e3aad3 100644 --- a/pkg/atomicbitops/atomic_bitops_amd64.s +++ b/pkg/atomicbitops/atomic_bitops_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/atomicbitops/atomic_bitops_common.go b/pkg/atomicbitops/atomic_bitops_common.go index 542ff4e83..b03242baa 100644 --- a/pkg/atomicbitops/atomic_bitops_common.go +++ b/pkg/atomicbitops/atomic_bitops_common.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/atomicbitops/atomic_bitops_test.go b/pkg/atomicbitops/atomic_bitops_test.go index ec0c07ee2..ee6207cb3 100644 --- a/pkg/atomicbitops/atomic_bitops_test.go +++ b/pkg/atomicbitops/atomic_bitops_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/binary/binary.go b/pkg/binary/binary.go index 3b18a86ee..02f7e9fb8 100644 --- a/pkg/binary/binary.go +++ b/pkg/binary/binary.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/binary/binary_test.go b/pkg/binary/binary_test.go index 921a0369a..d8d481f32 100644 --- a/pkg/binary/binary_test.go +++ b/pkg/binary/binary_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bits/bits.go b/pkg/bits/bits.go index 50ca4bff7..eb3c80f49 100644 --- a/pkg/bits/bits.go +++ b/pkg/bits/bits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bits/bits_template.go b/pkg/bits/bits_template.go index 0a01f29c2..8c578cca2 100644 --- a/pkg/bits/bits_template.go +++ b/pkg/bits/bits_template.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bits/uint64_arch_amd64.go b/pkg/bits/uint64_arch_amd64.go index 068597f68..1fef89394 100644 --- a/pkg/bits/uint64_arch_amd64.go +++ b/pkg/bits/uint64_arch_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bits/uint64_arch_amd64_asm.s b/pkg/bits/uint64_arch_amd64_asm.s index 33885641a..8c7322f0f 100644 --- a/pkg/bits/uint64_arch_amd64_asm.s +++ b/pkg/bits/uint64_arch_amd64_asm.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bits/uint64_arch_generic.go b/pkg/bits/uint64_arch_generic.go index 862033a4b..cfb47400b 100644 --- a/pkg/bits/uint64_arch_generic.go +++ b/pkg/bits/uint64_arch_generic.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bits/uint64_test.go b/pkg/bits/uint64_test.go index 906017e1a..d6dbaf602 100644 --- a/pkg/bits/uint64_test.go +++ b/pkg/bits/uint64_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/bpf.go b/pkg/bpf/bpf.go index 757744090..98d44d911 100644 --- a/pkg/bpf/bpf.go +++ b/pkg/bpf/bpf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/decoder.go b/pkg/bpf/decoder.go index ef41e9edc..ae6b8839a 100644 --- a/pkg/bpf/decoder.go +++ b/pkg/bpf/decoder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/decoder_test.go b/pkg/bpf/decoder_test.go index 18709b944..f093e1e41 100644 --- a/pkg/bpf/decoder_test.go +++ b/pkg/bpf/decoder_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/input_bytes.go b/pkg/bpf/input_bytes.go index 74af038eb..745c0749b 100644 --- a/pkg/bpf/input_bytes.go +++ b/pkg/bpf/input_bytes.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/interpreter.go b/pkg/bpf/interpreter.go index 111ada9d1..86c7add4d 100644 --- a/pkg/bpf/interpreter.go +++ b/pkg/bpf/interpreter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/interpreter_test.go b/pkg/bpf/interpreter_test.go index 9e5e33228..c46a43991 100644 --- a/pkg/bpf/interpreter_test.go +++ b/pkg/bpf/interpreter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/program_builder.go b/pkg/bpf/program_builder.go index bad56d7ac..b4ce228e1 100644 --- a/pkg/bpf/program_builder.go +++ b/pkg/bpf/program_builder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/bpf/program_builder_test.go b/pkg/bpf/program_builder_test.go index 7e4f06584..0e0b79d88 100644 --- a/pkg/bpf/program_builder_test.go +++ b/pkg/bpf/program_builder_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/compressio/compressio.go b/pkg/compressio/compressio.go index 667f17c5c..205536812 100644 --- a/pkg/compressio/compressio.go +++ b/pkg/compressio/compressio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/compressio/compressio_test.go b/pkg/compressio/compressio_test.go index 7cb5f8dc4..1bbabee79 100644 --- a/pkg/compressio/compressio_test.go +++ b/pkg/compressio/compressio_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/control/client/client.go b/pkg/control/client/client.go index f7c2e8776..0d0c9f148 100644 --- a/pkg/control/client/client.go +++ b/pkg/control/client/client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/control/server/server.go b/pkg/control/server/server.go index d00061ce3..c46b5d70b 100644 --- a/pkg/control/server/server.go +++ b/pkg/control/server/server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/cpuid/cpu_amd64.s b/pkg/cpuid/cpu_amd64.s index 48a13c6fd..905c1d12e 100644 --- a/pkg/cpuid/cpu_amd64.s +++ b/pkg/cpuid/cpu_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/cpuid/cpuid.go b/pkg/cpuid/cpuid.go index e91e34dc7..5b083a5fb 100644 --- a/pkg/cpuid/cpuid.go +++ b/pkg/cpuid/cpuid.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/cpuid/cpuid_parse_test.go b/pkg/cpuid/cpuid_parse_test.go index c4f52818c..81b06f48c 100644 --- a/pkg/cpuid/cpuid_parse_test.go +++ b/pkg/cpuid/cpuid_parse_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/cpuid/cpuid_test.go b/pkg/cpuid/cpuid_test.go index 02f732f85..0decd8f08 100644 --- a/pkg/cpuid/cpuid_test.go +++ b/pkg/cpuid/cpuid_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dhcp/client.go b/pkg/dhcp/client.go index 92c634a14..3330c4998 100644 --- a/pkg/dhcp/client.go +++ b/pkg/dhcp/client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dhcp/dhcp.go b/pkg/dhcp/dhcp.go index ceaba34c3..ad11e178a 100644 --- a/pkg/dhcp/dhcp.go +++ b/pkg/dhcp/dhcp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dhcp/dhcp_string.go b/pkg/dhcp/dhcp_string.go index 7cabed29e..8533895bd 100644 --- a/pkg/dhcp/dhcp_string.go +++ b/pkg/dhcp/dhcp_string.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dhcp/dhcp_test.go b/pkg/dhcp/dhcp_test.go index d60e3752b..a21dce6bc 100644 --- a/pkg/dhcp/dhcp_test.go +++ b/pkg/dhcp/dhcp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dhcp/server.go b/pkg/dhcp/server.go index 26700bdbc..3e06ab4c7 100644 --- a/pkg/dhcp/server.go +++ b/pkg/dhcp/server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/eventchannel/event.go b/pkg/eventchannel/event.go index bfd28256e..41a7b5ed3 100644 --- a/pkg/eventchannel/event.go +++ b/pkg/eventchannel/event.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/eventchannel/event.proto b/pkg/eventchannel/event.proto index 455f03658..c1679c7e7 100644 --- a/pkg/eventchannel/event.proto +++ b/pkg/eventchannel/event.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/fd/fd.go b/pkg/fd/fd.go index 32d24c41b..f6656ffa1 100644 --- a/pkg/fd/fd.go +++ b/pkg/fd/fd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/fd/fd_test.go b/pkg/fd/fd_test.go index 94b3eb7cc..42bb3ef6c 100644 --- a/pkg/fd/fd_test.go +++ b/pkg/fd/fd_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/gate/gate.go b/pkg/gate/gate.go index 93808c9dd..48122bf5a 100644 --- a/pkg/gate/gate.go +++ b/pkg/gate/gate.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/gate/gate_test.go b/pkg/gate/gate_test.go index 06587339b..95620fa8e 100644 --- a/pkg/gate/gate_test.go +++ b/pkg/gate/gate_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/ilist/list.go b/pkg/ilist/list.go index 4ae02eee9..51c9b6df3 100644 --- a/pkg/ilist/list.go +++ b/pkg/ilist/list.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/ilist/list_test.go b/pkg/ilist/list_test.go index 2c56280f6..4bda570b6 100644 --- a/pkg/ilist/list_test.go +++ b/pkg/ilist/list_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/linewriter/linewriter.go b/pkg/linewriter/linewriter.go index 98f974410..5fbd4e779 100644 --- a/pkg/linewriter/linewriter.go +++ b/pkg/linewriter/linewriter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/linewriter/linewriter_test.go b/pkg/linewriter/linewriter_test.go index ce97cca05..9140ee6af 100644 --- a/pkg/linewriter/linewriter_test.go +++ b/pkg/linewriter/linewriter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/log/glog.go b/pkg/log/glog.go index 58b4052e6..fbb58501b 100644 --- a/pkg/log/glog.go +++ b/pkg/log/glog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/log/glog_unsafe.go b/pkg/log/glog_unsafe.go index c320190b8..bb06aa7d3 100644 --- a/pkg/log/glog_unsafe.go +++ b/pkg/log/glog_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/log/json.go b/pkg/log/json.go index 3887f1cd5..96bd13d87 100644 --- a/pkg/log/json.go +++ b/pkg/log/json.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/log/json_test.go b/pkg/log/json_test.go index 3b167dab0..b8c7a795e 100644 --- a/pkg/log/json_test.go +++ b/pkg/log/json_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/log/log.go b/pkg/log/log.go index c496e86e4..b8d456aae 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go index d93e989dc..a59d457dd 100644 --- a/pkg/log/log_test.go +++ b/pkg/log/log_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/metric/metric.go b/pkg/metric/metric.go index 763cd6bc2..02af75974 100644 --- a/pkg/metric/metric.go +++ b/pkg/metric/metric.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/metric/metric.proto b/pkg/metric/metric.proto index 6108cb7c0..917fda1ac 100644 --- a/pkg/metric/metric.proto +++ b/pkg/metric/metric.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/metric/metric_test.go b/pkg/metric/metric_test.go index 7d156e4a5..40034a589 100644 --- a/pkg/metric/metric_test.go +++ b/pkg/metric/metric_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/buffer.go b/pkg/p9/buffer.go index fc65d2c5f..9575ddf12 100644 --- a/pkg/p9/buffer.go +++ b/pkg/p9/buffer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/client.go b/pkg/p9/client.go index 5fa231bc5..3ebfab82a 100644 --- a/pkg/p9/client.go +++ b/pkg/p9/client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/client_file.go b/pkg/p9/client_file.go index a46efd27f..066639fda 100644 --- a/pkg/p9/client_file.go +++ b/pkg/p9/client_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/client_test.go b/pkg/p9/client_test.go index 06302a76a..f7145452d 100644 --- a/pkg/p9/client_test.go +++ b/pkg/p9/client_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/file.go b/pkg/p9/file.go index 9723fa24d..d2e89e373 100644 --- a/pkg/p9/file.go +++ b/pkg/p9/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/handlers.go b/pkg/p9/handlers.go index ea41f97c7..959dff31d 100644 --- a/pkg/p9/handlers.go +++ b/pkg/p9/handlers.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/local_server/local_server.go b/pkg/p9/local_server/local_server.go index cef3701a7..1e6aaa762 100644 --- a/pkg/p9/local_server/local_server.go +++ b/pkg/p9/local_server/local_server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go index b3d76801b..972c37344 100644 --- a/pkg/p9/messages.go +++ b/pkg/p9/messages.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/messages_test.go b/pkg/p9/messages_test.go index f353755f1..dfb41bb76 100644 --- a/pkg/p9/messages_test.go +++ b/pkg/p9/messages_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/p9.go b/pkg/p9/p9.go index c6899c3ce..3b0993ecd 100644 --- a/pkg/p9/p9.go +++ b/pkg/p9/p9.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/p9_test.go b/pkg/p9/p9_test.go index a50ac80a4..02498346c 100644 --- a/pkg/p9/p9_test.go +++ b/pkg/p9/p9_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/p9test/client_test.go b/pkg/p9/p9test/client_test.go index 34ddccd8b..db562b9ba 100644 --- a/pkg/p9/p9test/client_test.go +++ b/pkg/p9/p9test/client_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/p9test/mocks.go b/pkg/p9/p9test/mocks.go index 9d039ac63..9a8c14975 100644 --- a/pkg/p9/p9test/mocks.go +++ b/pkg/p9/p9test/mocks.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/pool.go b/pkg/p9/pool.go index 9a508b898..34ed898e8 100644 --- a/pkg/p9/pool.go +++ b/pkg/p9/pool.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/pool_test.go b/pkg/p9/pool_test.go index 96be2c8bd..71052d8c4 100644 --- a/pkg/p9/pool_test.go +++ b/pkg/p9/pool_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/server.go b/pkg/p9/server.go index 28a273ac6..5c7cb18c8 100644 --- a/pkg/p9/server.go +++ b/pkg/p9/server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/transport.go b/pkg/p9/transport.go index b5df29961..97396806c 100644 --- a/pkg/p9/transport.go +++ b/pkg/p9/transport.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/transport_test.go b/pkg/p9/transport_test.go index d6d4b6365..3352a5205 100644 --- a/pkg/p9/transport_test.go +++ b/pkg/p9/transport_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/version.go b/pkg/p9/version.go index 8783eaa7e..ceb6fabbf 100644 --- a/pkg/p9/version.go +++ b/pkg/p9/version.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/p9/version_test.go b/pkg/p9/version_test.go index 634ac3ca5..c053614c9 100644 --- a/pkg/p9/version_test.go +++ b/pkg/p9/version_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/rand/rand.go b/pkg/rand/rand.go index e81f0f5db..593a14380 100644 --- a/pkg/rand/rand.go +++ b/pkg/rand/rand.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/rand/rand_linux.go b/pkg/rand/rand_linux.go index a2be66b3b..7ebe8f3b0 100644 --- a/pkg/rand/rand_linux.go +++ b/pkg/rand/rand_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/refs/refcounter.go b/pkg/refs/refcounter.go index 638a93bab..8f08c74c7 100644 --- a/pkg/refs/refcounter.go +++ b/pkg/refs/refcounter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/refs/refcounter_state.go b/pkg/refs/refcounter_state.go index 093eae785..136f06fbf 100644 --- a/pkg/refs/refcounter_state.go +++ b/pkg/refs/refcounter_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/refs/refcounter_test.go b/pkg/refs/refcounter_test.go index cc11bcd71..abaa87453 100644 --- a/pkg/refs/refcounter_test.go +++ b/pkg/refs/refcounter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go index a746dc9b3..1dfbf749e 100644 --- a/pkg/seccomp/seccomp.go +++ b/pkg/seccomp/seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/seccomp/seccomp_rules.go b/pkg/seccomp/seccomp_rules.go index 6b707f195..a9278c64b 100644 --- a/pkg/seccomp/seccomp_rules.go +++ b/pkg/seccomp/seccomp_rules.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/seccomp/seccomp_test.go b/pkg/seccomp/seccomp_test.go index 0188ad4f3..226f30b7b 100644 --- a/pkg/seccomp/seccomp_test.go +++ b/pkg/seccomp/seccomp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/seccomp/seccomp_test_victim.go b/pkg/seccomp/seccomp_test_victim.go index 4f2ae4dac..007038273 100644 --- a/pkg/seccomp/seccomp_test_victim.go +++ b/pkg/seccomp/seccomp_test_victim.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/seccomp/seccomp_unsafe.go b/pkg/seccomp/seccomp_unsafe.go index ae18534bf..dd009221a 100644 --- a/pkg/seccomp/seccomp_unsafe.go +++ b/pkg/seccomp/seccomp_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/secio/full_reader.go b/pkg/secio/full_reader.go index b2dbb8615..90b1772a7 100644 --- a/pkg/secio/full_reader.go +++ b/pkg/secio/full_reader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/secio/secio.go b/pkg/secio/secio.go index fc625efb8..e5f74a497 100644 --- a/pkg/secio/secio.go +++ b/pkg/secio/secio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/secio/secio_test.go b/pkg/secio/secio_test.go index 64b4cc17d..8304c4f74 100644 --- a/pkg/secio/secio_test.go +++ b/pkg/secio/secio_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/segment/range.go b/pkg/segment/range.go index 34c067265..057bcd7ff 100644 --- a/pkg/segment/range.go +++ b/pkg/segment/range.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/segment/set.go b/pkg/segment/set.go index cffec2a2c..a9a3b8875 100644 --- a/pkg/segment/set.go +++ b/pkg/segment/set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/segment/set_state.go b/pkg/segment/set_state.go index a763d1915..b86e1b75f 100644 --- a/pkg/segment/set_state.go +++ b/pkg/segment/set_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/segment/test/segment_test.go b/pkg/segment/test/segment_test.go index 7ea24b177..0825105db 100644 --- a/pkg/segment/test/segment_test.go +++ b/pkg/segment/test/segment_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/segment/test/set_functions.go b/pkg/segment/test/set_functions.go index 37c196ea1..05ba5fbb9 100644 --- a/pkg/segment/test/set_functions.go +++ b/pkg/segment/test/set_functions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/aligned.go b/pkg/sentry/arch/aligned.go index 193232e27..c88c034f6 100644 --- a/pkg/sentry/arch/aligned.go +++ b/pkg/sentry/arch/aligned.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/arch.go b/pkg/sentry/arch/arch.go index 21cb84502..575b7ba66 100644 --- a/pkg/sentry/arch/arch.go +++ b/pkg/sentry/arch/arch.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/arch_amd64.go b/pkg/sentry/arch/arch_amd64.go index 5ba6c19ea..bb80a7bed 100644 --- a/pkg/sentry/arch/arch_amd64.go +++ b/pkg/sentry/arch/arch_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/arch_amd64.s b/pkg/sentry/arch/arch_amd64.s index 10d621b6d..fa9857df7 100644 --- a/pkg/sentry/arch/arch_amd64.s +++ b/pkg/sentry/arch/arch_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/arch_state_x86.go b/pkg/sentry/arch/arch_state_x86.go index e9c23a06b..604bd08a6 100644 --- a/pkg/sentry/arch/arch_state_x86.go +++ b/pkg/sentry/arch/arch_state_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/arch_x86.go b/pkg/sentry/arch/arch_x86.go index b35eec53c..59bf89d99 100644 --- a/pkg/sentry/arch/arch_x86.go +++ b/pkg/sentry/arch/arch_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/auxv.go b/pkg/sentry/arch/auxv.go index 81cfb4a01..5df65a691 100644 --- a/pkg/sentry/arch/auxv.go +++ b/pkg/sentry/arch/auxv.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/registers.proto b/pkg/sentry/arch/registers.proto index 437ff44ca..f4c2f7043 100644 --- a/pkg/sentry/arch/registers.proto +++ b/pkg/sentry/arch/registers.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/signal_act.go b/pkg/sentry/arch/signal_act.go index 36437b965..ad098c746 100644 --- a/pkg/sentry/arch/signal_act.go +++ b/pkg/sentry/arch/signal_act.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/signal_amd64.go b/pkg/sentry/arch/signal_amd64.go index 9ca4c8ed1..f7f054b0b 100644 --- a/pkg/sentry/arch/signal_amd64.go +++ b/pkg/sentry/arch/signal_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/signal_info.go b/pkg/sentry/arch/signal_info.go index ec004ae75..fa0ecbec5 100644 --- a/pkg/sentry/arch/signal_info.go +++ b/pkg/sentry/arch/signal_info.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/signal_stack.go b/pkg/sentry/arch/signal_stack.go index ba43dd1d4..c02ae3b7c 100644 --- a/pkg/sentry/arch/signal_stack.go +++ b/pkg/sentry/arch/signal_stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/stack.go b/pkg/sentry/arch/stack.go index 6c1b9be82..716a3574d 100644 --- a/pkg/sentry/arch/stack.go +++ b/pkg/sentry/arch/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/arch/syscalls_amd64.go b/pkg/sentry/arch/syscalls_amd64.go index 41d8ba0d1..47c31d4b9 100644 --- a/pkg/sentry/arch/syscalls_amd64.go +++ b/pkg/sentry/arch/syscalls_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/context/context.go b/pkg/sentry/context/context.go index 598c5b4ff..12bdcef85 100644 --- a/pkg/sentry/context/context.go +++ b/pkg/sentry/context/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/context/contexttest/contexttest.go b/pkg/sentry/context/contexttest/contexttest.go index b3c6a566b..d2f084ed7 100644 --- a/pkg/sentry/context/contexttest/contexttest.go +++ b/pkg/sentry/context/contexttest/contexttest.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/control/control.go b/pkg/sentry/control/control.go index a6ee6e649..32d30b6ea 100644 --- a/pkg/sentry/control/control.go +++ b/pkg/sentry/control/control.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go index 0ba730c1e..b6ac2f312 100644 --- a/pkg/sentry/control/proc.go +++ b/pkg/sentry/control/proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/control/proc_test.go b/pkg/sentry/control/proc_test.go index 22c826236..5d52cd829 100644 --- a/pkg/sentry/control/proc_test.go +++ b/pkg/sentry/control/proc_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/control/state.go b/pkg/sentry/control/state.go index cee4db636..0a480c84a 100644 --- a/pkg/sentry/control/state.go +++ b/pkg/sentry/control/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/device/device.go b/pkg/sentry/device/device.go index 21fee8f8a..27e4eb258 100644 --- a/pkg/sentry/device/device.go +++ b/pkg/sentry/device/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/device/device_test.go b/pkg/sentry/device/device_test.go index dfec45046..5d8805c2f 100644 --- a/pkg/sentry/device/device_test.go +++ b/pkg/sentry/device/device_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/anon/anon.go b/pkg/sentry/fs/anon/anon.go index ddc2c0985..743cf511f 100644 --- a/pkg/sentry/fs/anon/anon.go +++ b/pkg/sentry/fs/anon/anon.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/anon/device.go b/pkg/sentry/fs/anon/device.go index 1c666729c..2d1249299 100644 --- a/pkg/sentry/fs/anon/device.go +++ b/pkg/sentry/fs/anon/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ashmem/area.go b/pkg/sentry/fs/ashmem/area.go index bfd7f2762..5372875ac 100644 --- a/pkg/sentry/fs/ashmem/area.go +++ b/pkg/sentry/fs/ashmem/area.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ashmem/device.go b/pkg/sentry/fs/ashmem/device.go index d0986fa11..962da141b 100644 --- a/pkg/sentry/fs/ashmem/device.go +++ b/pkg/sentry/fs/ashmem/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ashmem/pin_board.go b/pkg/sentry/fs/ashmem/pin_board.go index ecba395a0..7c997f533 100644 --- a/pkg/sentry/fs/ashmem/pin_board.go +++ b/pkg/sentry/fs/ashmem/pin_board.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ashmem/pin_board_test.go b/pkg/sentry/fs/ashmem/pin_board_test.go index f4ea5de6d..736e628dc 100644 --- a/pkg/sentry/fs/ashmem/pin_board_test.go +++ b/pkg/sentry/fs/ashmem/pin_board_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/attr.go b/pkg/sentry/fs/attr.go index 091f4ac63..59e060e3c 100644 --- a/pkg/sentry/fs/attr.go +++ b/pkg/sentry/fs/attr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/binder/binder.go b/pkg/sentry/fs/binder/binder.go index 502a262dd..42b9e8b26 100644 --- a/pkg/sentry/fs/binder/binder.go +++ b/pkg/sentry/fs/binder/binder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/context.go b/pkg/sentry/fs/context.go index da46ad77f..1775d3486 100644 --- a/pkg/sentry/fs/context.go +++ b/pkg/sentry/fs/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/copy_up.go b/pkg/sentry/fs/copy_up.go index 8c949b176..d65dc74bf 100644 --- a/pkg/sentry/fs/copy_up.go +++ b/pkg/sentry/fs/copy_up.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/copy_up_test.go b/pkg/sentry/fs/copy_up_test.go index c3c9d963d..64f030f72 100644 --- a/pkg/sentry/fs/copy_up_test.go +++ b/pkg/sentry/fs/copy_up_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dentry.go b/pkg/sentry/fs/dentry.go index b347468ff..ef6d1a870 100644 --- a/pkg/sentry/fs/dentry.go +++ b/pkg/sentry/fs/dentry.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dev/dev.go b/pkg/sentry/fs/dev/dev.go index 3f4f2a40a..05a5005ad 100644 --- a/pkg/sentry/fs/dev/dev.go +++ b/pkg/sentry/fs/dev/dev.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dev/device.go b/pkg/sentry/fs/dev/device.go index 9d935e008..3cecdf6e2 100644 --- a/pkg/sentry/fs/dev/device.go +++ b/pkg/sentry/fs/dev/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dev/fs.go b/pkg/sentry/fs/dev/fs.go index 2ae49be4e..d96f4f423 100644 --- a/pkg/sentry/fs/dev/fs.go +++ b/pkg/sentry/fs/dev/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dev/full.go b/pkg/sentry/fs/dev/full.go index 492b8eb3a..eeda646ab 100644 --- a/pkg/sentry/fs/dev/full.go +++ b/pkg/sentry/fs/dev/full.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dev/null.go b/pkg/sentry/fs/dev/null.go index 2977c8670..68090f353 100644 --- a/pkg/sentry/fs/dev/null.go +++ b/pkg/sentry/fs/dev/null.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dev/random.go b/pkg/sentry/fs/dev/random.go index 47b76218f..33e4913e4 100644 --- a/pkg/sentry/fs/dev/random.go +++ b/pkg/sentry/fs/dev/random.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dirent.go b/pkg/sentry/fs/dirent.go index 27fea0019..2c01485a8 100644 --- a/pkg/sentry/fs/dirent.go +++ b/pkg/sentry/fs/dirent.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dirent_cache.go b/pkg/sentry/fs/dirent_cache.go index c680e4828..502b0a09b 100644 --- a/pkg/sentry/fs/dirent_cache.go +++ b/pkg/sentry/fs/dirent_cache.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dirent_cache_test.go b/pkg/sentry/fs/dirent_cache_test.go index 82b7f6bd5..5d0e9d91c 100644 --- a/pkg/sentry/fs/dirent_cache_test.go +++ b/pkg/sentry/fs/dirent_cache_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dirent_refs_test.go b/pkg/sentry/fs/dirent_refs_test.go index f9dcba316..325404e27 100644 --- a/pkg/sentry/fs/dirent_refs_test.go +++ b/pkg/sentry/fs/dirent_refs_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/dirent_state.go b/pkg/sentry/fs/dirent_state.go index 04ab197b9..5cf151dab 100644 --- a/pkg/sentry/fs/dirent_state.go +++ b/pkg/sentry/fs/dirent_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fdpipe/pipe.go b/pkg/sentry/fs/fdpipe/pipe.go index 2e34604e6..bfafff5ec 100644 --- a/pkg/sentry/fs/fdpipe/pipe.go +++ b/pkg/sentry/fs/fdpipe/pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fdpipe/pipe_opener.go b/pkg/sentry/fs/fdpipe/pipe_opener.go index 945cfaf08..92ab6ff0e 100644 --- a/pkg/sentry/fs/fdpipe/pipe_opener.go +++ b/pkg/sentry/fs/fdpipe/pipe_opener.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fdpipe/pipe_opener_test.go b/pkg/sentry/fs/fdpipe/pipe_opener_test.go index 83f6c1986..69516e048 100644 --- a/pkg/sentry/fs/fdpipe/pipe_opener_test.go +++ b/pkg/sentry/fs/fdpipe/pipe_opener_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fdpipe/pipe_state.go b/pkg/sentry/fs/fdpipe/pipe_state.go index 99c40d8ed..4395666ad 100644 --- a/pkg/sentry/fs/fdpipe/pipe_state.go +++ b/pkg/sentry/fs/fdpipe/pipe_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fdpipe/pipe_test.go b/pkg/sentry/fs/fdpipe/pipe_test.go index 6cd314f5b..d3f15be6b 100644 --- a/pkg/sentry/fs/fdpipe/pipe_test.go +++ b/pkg/sentry/fs/fdpipe/pipe_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/file.go b/pkg/sentry/fs/file.go index 36794d378..d6752ed1b 100644 --- a/pkg/sentry/fs/file.go +++ b/pkg/sentry/fs/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/file_operations.go b/pkg/sentry/fs/file_operations.go index d223bb5c7..28e8e233d 100644 --- a/pkg/sentry/fs/file_operations.go +++ b/pkg/sentry/fs/file_operations.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/file_overlay.go b/pkg/sentry/fs/file_overlay.go index 41e646ee8..9b958b64b 100644 --- a/pkg/sentry/fs/file_overlay.go +++ b/pkg/sentry/fs/file_overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/file_overlay_test.go b/pkg/sentry/fs/file_overlay_test.go index 830458ff9..11e4f7203 100644 --- a/pkg/sentry/fs/file_overlay_test.go +++ b/pkg/sentry/fs/file_overlay_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/file_state.go b/pkg/sentry/fs/file_state.go index f848d1b79..1c3bae3e8 100644 --- a/pkg/sentry/fs/file_state.go +++ b/pkg/sentry/fs/file_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/file_test.go b/pkg/sentry/fs/file_test.go index 18aee7101..f3ed9a70b 100644 --- a/pkg/sentry/fs/file_test.go +++ b/pkg/sentry/fs/file_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/filesystems.go b/pkg/sentry/fs/filesystems.go index 5a1e7a270..ba8be85e4 100644 --- a/pkg/sentry/fs/filesystems.go +++ b/pkg/sentry/fs/filesystems.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/filetest/filetest.go b/pkg/sentry/fs/filetest/filetest.go index 1831aa82f..65ca196d9 100644 --- a/pkg/sentry/fs/filetest/filetest.go +++ b/pkg/sentry/fs/filetest/filetest.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/flags.go b/pkg/sentry/fs/flags.go index 1aa271560..bf2a20b33 100644 --- a/pkg/sentry/fs/flags.go +++ b/pkg/sentry/fs/flags.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fs.go b/pkg/sentry/fs/fs.go index 6ec9ff446..b5c72990e 100644 --- a/pkg/sentry/fs/fs.go +++ b/pkg/sentry/fs/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/dirty_set.go b/pkg/sentry/fs/fsutil/dirty_set.go index 8e31e48fd..5add16ac4 100644 --- a/pkg/sentry/fs/fsutil/dirty_set.go +++ b/pkg/sentry/fs/fsutil/dirty_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/dirty_set_test.go b/pkg/sentry/fs/fsutil/dirty_set_test.go index f7693cb19..f5c9d9215 100644 --- a/pkg/sentry/fs/fsutil/dirty_set_test.go +++ b/pkg/sentry/fs/fsutil/dirty_set_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/file.go b/pkg/sentry/fs/fsutil/file.go index d5881613b..46db2e51c 100644 --- a/pkg/sentry/fs/fsutil/file.go +++ b/pkg/sentry/fs/fsutil/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/file_range_set.go b/pkg/sentry/fs/fsutil/file_range_set.go index da6949ccb..dd7ab4b4a 100644 --- a/pkg/sentry/fs/fsutil/file_range_set.go +++ b/pkg/sentry/fs/fsutil/file_range_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/frame_ref_set.go b/pkg/sentry/fs/fsutil/frame_ref_set.go index 14dece315..b6e783614 100644 --- a/pkg/sentry/fs/fsutil/frame_ref_set.go +++ b/pkg/sentry/fs/fsutil/frame_ref_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/fsutil.go b/pkg/sentry/fs/fsutil/fsutil.go index 6fe4ef13d..3d7f3732d 100644 --- a/pkg/sentry/fs/fsutil/fsutil.go +++ b/pkg/sentry/fs/fsutil/fsutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/handle.go b/pkg/sentry/fs/fsutil/handle.go index e7efd3c0f..8920b72ee 100644 --- a/pkg/sentry/fs/fsutil/handle.go +++ b/pkg/sentry/fs/fsutil/handle.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/handle_test.go b/pkg/sentry/fs/fsutil/handle_test.go index d94c3eb0d..43e1a3bdf 100644 --- a/pkg/sentry/fs/fsutil/handle_test.go +++ b/pkg/sentry/fs/fsutil/handle_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/host_file_mapper.go b/pkg/sentry/fs/fsutil/host_file_mapper.go index 9c1e2f76f..9599665f0 100644 --- a/pkg/sentry/fs/fsutil/host_file_mapper.go +++ b/pkg/sentry/fs/fsutil/host_file_mapper.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/host_file_mapper_state.go b/pkg/sentry/fs/fsutil/host_file_mapper_state.go index 57705decd..bbd15b30b 100644 --- a/pkg/sentry/fs/fsutil/host_file_mapper_state.go +++ b/pkg/sentry/fs/fsutil/host_file_mapper_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go b/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go index 790f3a5a6..86df76822 100644 --- a/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go +++ b/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/inode.go b/pkg/sentry/fs/fsutil/inode.go index 3acc32752..d4db1c2de 100644 --- a/pkg/sentry/fs/fsutil/inode.go +++ b/pkg/sentry/fs/fsutil/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/inode_cached.go b/pkg/sentry/fs/fsutil/inode_cached.go index 6777c8bf7..b0af44ddd 100644 --- a/pkg/sentry/fs/fsutil/inode_cached.go +++ b/pkg/sentry/fs/fsutil/inode_cached.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/fsutil/inode_cached_test.go b/pkg/sentry/fs/fsutil/inode_cached_test.go index 996c91849..e388ec3d7 100644 --- a/pkg/sentry/fs/fsutil/inode_cached_test.go +++ b/pkg/sentry/fs/fsutil/inode_cached_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/attr.go b/pkg/sentry/fs/gofer/attr.go index 5e24767f9..98700d014 100644 --- a/pkg/sentry/fs/gofer/attr.go +++ b/pkg/sentry/fs/gofer/attr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/cache_policy.go b/pkg/sentry/fs/gofer/cache_policy.go index 98f43c578..3d380f0e8 100644 --- a/pkg/sentry/fs/gofer/cache_policy.go +++ b/pkg/sentry/fs/gofer/cache_policy.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/context_file.go b/pkg/sentry/fs/gofer/context_file.go index d4b6f6eb7..a0265c2aa 100644 --- a/pkg/sentry/fs/gofer/context_file.go +++ b/pkg/sentry/fs/gofer/context_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/device.go b/pkg/sentry/fs/gofer/device.go index fac7306d4..52c5acf48 100644 --- a/pkg/sentry/fs/gofer/device.go +++ b/pkg/sentry/fs/gofer/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/file.go b/pkg/sentry/fs/gofer/file.go index c4a210656..6d961813d 100644 --- a/pkg/sentry/fs/gofer/file.go +++ b/pkg/sentry/fs/gofer/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/file_state.go b/pkg/sentry/fs/gofer/file_state.go index 715af8f16..dd4f817bf 100644 --- a/pkg/sentry/fs/gofer/file_state.go +++ b/pkg/sentry/fs/gofer/file_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/fs.go b/pkg/sentry/fs/gofer/fs.go index 3ae93f059..ed30cb1f1 100644 --- a/pkg/sentry/fs/gofer/fs.go +++ b/pkg/sentry/fs/gofer/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/gofer_test.go b/pkg/sentry/fs/gofer/gofer_test.go index c8d7bd773..3190d1e18 100644 --- a/pkg/sentry/fs/gofer/gofer_test.go +++ b/pkg/sentry/fs/gofer/gofer_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/handles.go b/pkg/sentry/fs/gofer/handles.go index a3e52aad6..f32e99ce0 100644 --- a/pkg/sentry/fs/gofer/handles.go +++ b/pkg/sentry/fs/gofer/handles.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/inode.go b/pkg/sentry/fs/gofer/inode.go index 7fc8f77b0..5811b8b12 100644 --- a/pkg/sentry/fs/gofer/inode.go +++ b/pkg/sentry/fs/gofer/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/inode_state.go b/pkg/sentry/fs/gofer/inode_state.go index ad11034f9..ad4d3df58 100644 --- a/pkg/sentry/fs/gofer/inode_state.go +++ b/pkg/sentry/fs/gofer/inode_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/path.go b/pkg/sentry/fs/gofer/path.go index 0bf7881da..a324dc990 100644 --- a/pkg/sentry/fs/gofer/path.go +++ b/pkg/sentry/fs/gofer/path.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/session.go b/pkg/sentry/fs/gofer/session.go index 4e2293398..7552216f3 100644 --- a/pkg/sentry/fs/gofer/session.go +++ b/pkg/sentry/fs/gofer/session.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/session_state.go b/pkg/sentry/fs/gofer/session_state.go index 8e6424492..f657135fc 100644 --- a/pkg/sentry/fs/gofer/session_state.go +++ b/pkg/sentry/fs/gofer/session_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/socket.go b/pkg/sentry/fs/gofer/socket.go index d072da624..76ce58810 100644 --- a/pkg/sentry/fs/gofer/socket.go +++ b/pkg/sentry/fs/gofer/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/gofer/util.go b/pkg/sentry/fs/gofer/util.go index d9ed8c81e..1a759370d 100644 --- a/pkg/sentry/fs/gofer/util.go +++ b/pkg/sentry/fs/gofer/util.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/control.go b/pkg/sentry/fs/host/control.go index d2e34a69d..0753640a2 100644 --- a/pkg/sentry/fs/host/control.go +++ b/pkg/sentry/fs/host/control.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/descriptor.go b/pkg/sentry/fs/host/descriptor.go index 148291ba6..7c9d2b299 100644 --- a/pkg/sentry/fs/host/descriptor.go +++ b/pkg/sentry/fs/host/descriptor.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/descriptor_state.go b/pkg/sentry/fs/host/descriptor_state.go index 7fb274451..530c0109f 100644 --- a/pkg/sentry/fs/host/descriptor_state.go +++ b/pkg/sentry/fs/host/descriptor_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/descriptor_test.go b/pkg/sentry/fs/host/descriptor_test.go index f393a8b54..6bc1bd2ae 100644 --- a/pkg/sentry/fs/host/descriptor_test.go +++ b/pkg/sentry/fs/host/descriptor_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/device.go b/pkg/sentry/fs/host/device.go index f2a0b6b15..b5adedf44 100644 --- a/pkg/sentry/fs/host/device.go +++ b/pkg/sentry/fs/host/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/file.go b/pkg/sentry/fs/host/file.go index 22a5d9f12..975084c86 100644 --- a/pkg/sentry/fs/host/file.go +++ b/pkg/sentry/fs/host/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/fs.go b/pkg/sentry/fs/host/fs.go index e46ae433c..fec890964 100644 --- a/pkg/sentry/fs/host/fs.go +++ b/pkg/sentry/fs/host/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/fs_test.go b/pkg/sentry/fs/host/fs_test.go index b08125ca8..e69559aac 100644 --- a/pkg/sentry/fs/host/fs_test.go +++ b/pkg/sentry/fs/host/fs_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/inode.go b/pkg/sentry/fs/host/inode.go index e32497203..08754bd6b 100644 --- a/pkg/sentry/fs/host/inode.go +++ b/pkg/sentry/fs/host/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/inode_state.go b/pkg/sentry/fs/host/inode_state.go index 8bc99d94b..b7c1a9581 100644 --- a/pkg/sentry/fs/host/inode_state.go +++ b/pkg/sentry/fs/host/inode_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/inode_test.go b/pkg/sentry/fs/host/inode_test.go index 0ff87c418..9f1561bd5 100644 --- a/pkg/sentry/fs/host/inode_test.go +++ b/pkg/sentry/fs/host/inode_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/ioctl_unsafe.go b/pkg/sentry/fs/host/ioctl_unsafe.go index bc965a1c2..175dca613 100644 --- a/pkg/sentry/fs/host/ioctl_unsafe.go +++ b/pkg/sentry/fs/host/ioctl_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/socket.go b/pkg/sentry/fs/host/socket.go index 0eb267c00..af53bf533 100644 --- a/pkg/sentry/fs/host/socket.go +++ b/pkg/sentry/fs/host/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/socket_iovec.go b/pkg/sentry/fs/host/socket_iovec.go index 1a9587b90..d4ce4a8c1 100644 --- a/pkg/sentry/fs/host/socket_iovec.go +++ b/pkg/sentry/fs/host/socket_iovec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/socket_state.go b/pkg/sentry/fs/host/socket_state.go index 7fa500bfb..2932c1f16 100644 --- a/pkg/sentry/fs/host/socket_state.go +++ b/pkg/sentry/fs/host/socket_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/socket_test.go b/pkg/sentry/fs/host/socket_test.go index 483e99dd6..e9a88b124 100644 --- a/pkg/sentry/fs/host/socket_test.go +++ b/pkg/sentry/fs/host/socket_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/socket_unsafe.go b/pkg/sentry/fs/host/socket_unsafe.go index 5e4c5feed..f35e2492d 100644 --- a/pkg/sentry/fs/host/socket_unsafe.go +++ b/pkg/sentry/fs/host/socket_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/tty.go b/pkg/sentry/fs/host/tty.go index ad1323610..cf3639c46 100644 --- a/pkg/sentry/fs/host/tty.go +++ b/pkg/sentry/fs/host/tty.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/util.go b/pkg/sentry/fs/host/util.go index 74c703eb7..40c450660 100644 --- a/pkg/sentry/fs/host/util.go +++ b/pkg/sentry/fs/host/util.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/util_unsafe.go b/pkg/sentry/fs/host/util_unsafe.go index 2ecb54319..d00da89d6 100644 --- a/pkg/sentry/fs/host/util_unsafe.go +++ b/pkg/sentry/fs/host/util_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/host/wait_test.go b/pkg/sentry/fs/host/wait_test.go index c5f5c9c0d..9ca8c399f 100644 --- a/pkg/sentry/fs/host/wait_test.go +++ b/pkg/sentry/fs/host/wait_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inode.go b/pkg/sentry/fs/inode.go index 409c81a97..95769ccf8 100644 --- a/pkg/sentry/fs/inode.go +++ b/pkg/sentry/fs/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inode_inotify.go b/pkg/sentry/fs/inode_inotify.go index 683140afe..e213df924 100644 --- a/pkg/sentry/fs/inode_inotify.go +++ b/pkg/sentry/fs/inode_inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inode_operations.go b/pkg/sentry/fs/inode_operations.go index 3ee3de10e..77973ce79 100644 --- a/pkg/sentry/fs/inode_operations.go +++ b/pkg/sentry/fs/inode_operations.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inode_overlay.go b/pkg/sentry/fs/inode_overlay.go index cf698a4da..78923fb5b 100644 --- a/pkg/sentry/fs/inode_overlay.go +++ b/pkg/sentry/fs/inode_overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inode_overlay_test.go b/pkg/sentry/fs/inode_overlay_test.go index 23e5635a4..bba20da14 100644 --- a/pkg/sentry/fs/inode_overlay_test.go +++ b/pkg/sentry/fs/inode_overlay_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inotify.go b/pkg/sentry/fs/inotify.go index 2aabdded8..f251df0d1 100644 --- a/pkg/sentry/fs/inotify.go +++ b/pkg/sentry/fs/inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inotify_event.go b/pkg/sentry/fs/inotify_event.go index e9b5e0f56..9e3e9d816 100644 --- a/pkg/sentry/fs/inotify_event.go +++ b/pkg/sentry/fs/inotify_event.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/inotify_watch.go b/pkg/sentry/fs/inotify_watch.go index 3e1959e83..b83544c9f 100644 --- a/pkg/sentry/fs/inotify_watch.go +++ b/pkg/sentry/fs/inotify_watch.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/lock/lock.go b/pkg/sentry/fs/lock/lock.go index 439e645db..5ff800d2d 100644 --- a/pkg/sentry/fs/lock/lock.go +++ b/pkg/sentry/fs/lock/lock.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/lock/lock_range_test.go b/pkg/sentry/fs/lock/lock_range_test.go index 06a37c701..b0ab882b9 100644 --- a/pkg/sentry/fs/lock/lock_range_test.go +++ b/pkg/sentry/fs/lock/lock_range_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/lock/lock_set_functions.go b/pkg/sentry/fs/lock/lock_set_functions.go index e16f485be..395592a4b 100644 --- a/pkg/sentry/fs/lock/lock_set_functions.go +++ b/pkg/sentry/fs/lock/lock_set_functions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/lock/lock_test.go b/pkg/sentry/fs/lock/lock_test.go index c60f5f7a2..67fa4b1dd 100644 --- a/pkg/sentry/fs/lock/lock_test.go +++ b/pkg/sentry/fs/lock/lock_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/mock.go b/pkg/sentry/fs/mock.go index 846b6e8bb..6bfcda6bb 100644 --- a/pkg/sentry/fs/mock.go +++ b/pkg/sentry/fs/mock.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/mount.go b/pkg/sentry/fs/mount.go index 8345876fc..24e28ddb2 100644 --- a/pkg/sentry/fs/mount.go +++ b/pkg/sentry/fs/mount.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/mount_overlay.go b/pkg/sentry/fs/mount_overlay.go index dbc608c7e..fb91635bc 100644 --- a/pkg/sentry/fs/mount_overlay.go +++ b/pkg/sentry/fs/mount_overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/mount_state.go b/pkg/sentry/fs/mount_state.go index f5ed1dd8d..6344d5160 100644 --- a/pkg/sentry/fs/mount_state.go +++ b/pkg/sentry/fs/mount_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/mount_test.go b/pkg/sentry/fs/mount_test.go index 968b435ab..a1c9f4f79 100644 --- a/pkg/sentry/fs/mount_test.go +++ b/pkg/sentry/fs/mount_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/mounts.go b/pkg/sentry/fs/mounts.go index c0a803b2d..7c5348cce 100644 --- a/pkg/sentry/fs/mounts.go +++ b/pkg/sentry/fs/mounts.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/mounts_test.go b/pkg/sentry/fs/mounts_test.go index 8669f3a38..cc7c32c9b 100644 --- a/pkg/sentry/fs/mounts_test.go +++ b/pkg/sentry/fs/mounts_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/offset.go b/pkg/sentry/fs/offset.go index 7cc8398e6..38aee765a 100644 --- a/pkg/sentry/fs/offset.go +++ b/pkg/sentry/fs/offset.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/overlay.go b/pkg/sentry/fs/overlay.go index 5a30af419..036c0f733 100644 --- a/pkg/sentry/fs/overlay.go +++ b/pkg/sentry/fs/overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/path.go b/pkg/sentry/fs/path.go index b74f6ed8c..91a9a8ffd 100644 --- a/pkg/sentry/fs/path.go +++ b/pkg/sentry/fs/path.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/path_test.go b/pkg/sentry/fs/path_test.go index 7ab070855..391b010a7 100644 --- a/pkg/sentry/fs/path_test.go +++ b/pkg/sentry/fs/path_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/cpuinfo.go b/pkg/sentry/fs/proc/cpuinfo.go index 4dfec03a4..f8be06dc3 100644 --- a/pkg/sentry/fs/proc/cpuinfo.go +++ b/pkg/sentry/fs/proc/cpuinfo.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/device/device.go b/pkg/sentry/fs/proc/device/device.go index 6194afe88..04b687bcf 100644 --- a/pkg/sentry/fs/proc/device/device.go +++ b/pkg/sentry/fs/proc/device/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/exec_args.go b/pkg/sentry/fs/proc/exec_args.go index a69cbaa0e..b4896053f 100644 --- a/pkg/sentry/fs/proc/exec_args.go +++ b/pkg/sentry/fs/proc/exec_args.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/fds.go b/pkg/sentry/fs/proc/fds.go index dada8f982..5ebb33703 100644 --- a/pkg/sentry/fs/proc/fds.go +++ b/pkg/sentry/fs/proc/fds.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/file.go b/pkg/sentry/fs/proc/file.go index 4b3448245..f659e590a 100644 --- a/pkg/sentry/fs/proc/file.go +++ b/pkg/sentry/fs/proc/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/filesystems.go b/pkg/sentry/fs/proc/filesystems.go index 49b92fd8a..c050a00be 100644 --- a/pkg/sentry/fs/proc/filesystems.go +++ b/pkg/sentry/fs/proc/filesystems.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/fs.go b/pkg/sentry/fs/proc/fs.go index 061824b8c..63f737ff4 100644 --- a/pkg/sentry/fs/proc/fs.go +++ b/pkg/sentry/fs/proc/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/loadavg.go b/pkg/sentry/fs/proc/loadavg.go index 6fac251d2..78f3a1dc0 100644 --- a/pkg/sentry/fs/proc/loadavg.go +++ b/pkg/sentry/fs/proc/loadavg.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/meminfo.go b/pkg/sentry/fs/proc/meminfo.go index 53dfd59ef..b31258eed 100644 --- a/pkg/sentry/fs/proc/meminfo.go +++ b/pkg/sentry/fs/proc/meminfo.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/mounts.go b/pkg/sentry/fs/proc/mounts.go index 81dcc153a..0b0e87528 100644 --- a/pkg/sentry/fs/proc/mounts.go +++ b/pkg/sentry/fs/proc/mounts.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/net.go b/pkg/sentry/fs/proc/net.go index 8cd6fe9d3..45f2a1211 100644 --- a/pkg/sentry/fs/proc/net.go +++ b/pkg/sentry/fs/proc/net.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/net_test.go b/pkg/sentry/fs/proc/net_test.go index a31a20494..94677cc1d 100644 --- a/pkg/sentry/fs/proc/net_test.go +++ b/pkg/sentry/fs/proc/net_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/proc.go b/pkg/sentry/fs/proc/proc.go index 07029a7bb..33030bebf 100644 --- a/pkg/sentry/fs/proc/proc.go +++ b/pkg/sentry/fs/proc/proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/rpcinet_proc.go b/pkg/sentry/fs/proc/rpcinet_proc.go index 50d0271f9..d025069df 100644 --- a/pkg/sentry/fs/proc/rpcinet_proc.go +++ b/pkg/sentry/fs/proc/rpcinet_proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/seqfile/seqfile.go b/pkg/sentry/fs/proc/seqfile/seqfile.go index 51cae5e37..0499ba65b 100644 --- a/pkg/sentry/fs/proc/seqfile/seqfile.go +++ b/pkg/sentry/fs/proc/seqfile/seqfile.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/seqfile/seqfile_test.go b/pkg/sentry/fs/proc/seqfile/seqfile_test.go index d90e3e736..f9a2ca38e 100644 --- a/pkg/sentry/fs/proc/seqfile/seqfile_test.go +++ b/pkg/sentry/fs/proc/seqfile/seqfile_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/stat.go b/pkg/sentry/fs/proc/stat.go index bf7650211..f2bbef375 100644 --- a/pkg/sentry/fs/proc/stat.go +++ b/pkg/sentry/fs/proc/stat.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/sys.go b/pkg/sentry/fs/proc/sys.go index 384b4ffe1..54562508d 100644 --- a/pkg/sentry/fs/proc/sys.go +++ b/pkg/sentry/fs/proc/sys.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/sys_net.go b/pkg/sentry/fs/proc/sys_net.go index beb25be20..801eb6a1e 100644 --- a/pkg/sentry/fs/proc/sys_net.go +++ b/pkg/sentry/fs/proc/sys_net.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/sys_net_test.go b/pkg/sentry/fs/proc/sys_net_test.go index 7ba392346..0ce9d30f1 100644 --- a/pkg/sentry/fs/proc/sys_net_test.go +++ b/pkg/sentry/fs/proc/sys_net_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/task.go b/pkg/sentry/fs/proc/task.go index 748ca4320..404faea0a 100644 --- a/pkg/sentry/fs/proc/task.go +++ b/pkg/sentry/fs/proc/task.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/uid_gid_map.go b/pkg/sentry/fs/proc/uid_gid_map.go index a7e4cf0a6..f70399686 100644 --- a/pkg/sentry/fs/proc/uid_gid_map.go +++ b/pkg/sentry/fs/proc/uid_gid_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/uptime.go b/pkg/sentry/fs/proc/uptime.go index f3a9b81df..80c7ce0b4 100644 --- a/pkg/sentry/fs/proc/uptime.go +++ b/pkg/sentry/fs/proc/uptime.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/proc/version.go b/pkg/sentry/fs/proc/version.go index 00f6a2afd..b6d49d5e9 100644 --- a/pkg/sentry/fs/proc/version.go +++ b/pkg/sentry/fs/proc/version.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/dir.go b/pkg/sentry/fs/ramfs/dir.go index 075e13b01..0a911b155 100644 --- a/pkg/sentry/fs/ramfs/dir.go +++ b/pkg/sentry/fs/ramfs/dir.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/file.go b/pkg/sentry/fs/ramfs/file.go index 0b94d92a1..b7fc98ffc 100644 --- a/pkg/sentry/fs/ramfs/file.go +++ b/pkg/sentry/fs/ramfs/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/ramfs.go b/pkg/sentry/fs/ramfs/ramfs.go index 83cbcab23..d77688a34 100644 --- a/pkg/sentry/fs/ramfs/ramfs.go +++ b/pkg/sentry/fs/ramfs/ramfs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/socket.go b/pkg/sentry/fs/ramfs/socket.go index 9ac00eb18..8c81478c8 100644 --- a/pkg/sentry/fs/ramfs/socket.go +++ b/pkg/sentry/fs/ramfs/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/symlink.go b/pkg/sentry/fs/ramfs/symlink.go index 1c54d9991..a21fac2c7 100644 --- a/pkg/sentry/fs/ramfs/symlink.go +++ b/pkg/sentry/fs/ramfs/symlink.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/test/test.go b/pkg/sentry/fs/ramfs/test/test.go index fb669558f..11bff7729 100644 --- a/pkg/sentry/fs/ramfs/test/test.go +++ b/pkg/sentry/fs/ramfs/test/test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/tree.go b/pkg/sentry/fs/ramfs/tree.go index 1fb335f74..29a70f698 100644 --- a/pkg/sentry/fs/ramfs/tree.go +++ b/pkg/sentry/fs/ramfs/tree.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/ramfs/tree_test.go b/pkg/sentry/fs/ramfs/tree_test.go index 68e2929d5..d5567d9e1 100644 --- a/pkg/sentry/fs/ramfs/tree_test.go +++ b/pkg/sentry/fs/ramfs/tree_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/restore.go b/pkg/sentry/fs/restore.go index b4ac85a27..da2df7e1d 100644 --- a/pkg/sentry/fs/restore.go +++ b/pkg/sentry/fs/restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/save.go b/pkg/sentry/fs/save.go index bf2a85143..90988d385 100644 --- a/pkg/sentry/fs/save.go +++ b/pkg/sentry/fs/save.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/seek.go b/pkg/sentry/fs/seek.go index 1268726c2..72f3fb632 100644 --- a/pkg/sentry/fs/seek.go +++ b/pkg/sentry/fs/seek.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/sync.go b/pkg/sentry/fs/sync.go index 9738a8f22..6dcc2fe8d 100644 --- a/pkg/sentry/fs/sync.go +++ b/pkg/sentry/fs/sync.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/sys/device.go b/pkg/sentry/fs/sys/device.go index 54e414d1b..38ecd0c18 100644 --- a/pkg/sentry/fs/sys/device.go +++ b/pkg/sentry/fs/sys/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/sys/devices.go b/pkg/sentry/fs/sys/devices.go index 2cf3a6f98..e64aa0edc 100644 --- a/pkg/sentry/fs/sys/devices.go +++ b/pkg/sentry/fs/sys/devices.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/sys/fs.go b/pkg/sentry/fs/sys/fs.go index 625525540..5ce33f87f 100644 --- a/pkg/sentry/fs/sys/fs.go +++ b/pkg/sentry/fs/sys/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/sys/sys.go b/pkg/sentry/fs/sys/sys.go index 7b9697668..7cc1942c7 100644 --- a/pkg/sentry/fs/sys/sys.go +++ b/pkg/sentry/fs/sys/sys.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/timerfd/timerfd.go b/pkg/sentry/fs/timerfd/timerfd.go index 767db95a0..7423e816c 100644 --- a/pkg/sentry/fs/timerfd/timerfd.go +++ b/pkg/sentry/fs/timerfd/timerfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tmpfs/device.go b/pkg/sentry/fs/tmpfs/device.go index e588b3440..aade93c26 100644 --- a/pkg/sentry/fs/tmpfs/device.go +++ b/pkg/sentry/fs/tmpfs/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tmpfs/file_regular.go b/pkg/sentry/fs/tmpfs/file_regular.go index 342688f81..1f9d69909 100644 --- a/pkg/sentry/fs/tmpfs/file_regular.go +++ b/pkg/sentry/fs/tmpfs/file_regular.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tmpfs/file_test.go b/pkg/sentry/fs/tmpfs/file_test.go index f064eb1ac..b5830d3df 100644 --- a/pkg/sentry/fs/tmpfs/file_test.go +++ b/pkg/sentry/fs/tmpfs/file_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tmpfs/fs.go b/pkg/sentry/fs/tmpfs/fs.go index ca620e65e..7c91e248b 100644 --- a/pkg/sentry/fs/tmpfs/fs.go +++ b/pkg/sentry/fs/tmpfs/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tmpfs/inode_file.go b/pkg/sentry/fs/tmpfs/inode_file.go index 1e4fe47d2..42a7d7b9c 100644 --- a/pkg/sentry/fs/tmpfs/inode_file.go +++ b/pkg/sentry/fs/tmpfs/inode_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tmpfs/tmpfs.go b/pkg/sentry/fs/tmpfs/tmpfs.go index 38be6db46..91b782540 100644 --- a/pkg/sentry/fs/tmpfs/tmpfs.go +++ b/pkg/sentry/fs/tmpfs/tmpfs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/dir.go b/pkg/sentry/fs/tty/dir.go index 7c0c0b0c1..e32b05c1d 100644 --- a/pkg/sentry/fs/tty/dir.go +++ b/pkg/sentry/fs/tty/dir.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/fs.go b/pkg/sentry/fs/tty/fs.go index d9f8f02f3..0c412eb21 100644 --- a/pkg/sentry/fs/tty/fs.go +++ b/pkg/sentry/fs/tty/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/inode.go b/pkg/sentry/fs/tty/inode.go index c0fa2b407..d5d1caafc 100644 --- a/pkg/sentry/fs/tty/inode.go +++ b/pkg/sentry/fs/tty/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/line_discipline.go b/pkg/sentry/fs/tty/line_discipline.go index 31804571e..484366f85 100644 --- a/pkg/sentry/fs/tty/line_discipline.go +++ b/pkg/sentry/fs/tty/line_discipline.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/master.go b/pkg/sentry/fs/tty/master.go index ae7540eff..dad0cad79 100644 --- a/pkg/sentry/fs/tty/master.go +++ b/pkg/sentry/fs/tty/master.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/queue.go b/pkg/sentry/fs/tty/queue.go index 01dc8d1ac..a09ca0119 100644 --- a/pkg/sentry/fs/tty/queue.go +++ b/pkg/sentry/fs/tty/queue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/slave.go b/pkg/sentry/fs/tty/slave.go index 4a0d4fdb9..9de3168bf 100644 --- a/pkg/sentry/fs/tty/slave.go +++ b/pkg/sentry/fs/tty/slave.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/terminal.go b/pkg/sentry/fs/tty/terminal.go index 3cb135124..79f9d76d7 100644 --- a/pkg/sentry/fs/tty/terminal.go +++ b/pkg/sentry/fs/tty/terminal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/fs/tty/tty_test.go b/pkg/sentry/fs/tty/tty_test.go index 32e1b1556..ad535838f 100644 --- a/pkg/sentry/fs/tty/tty_test.go +++ b/pkg/sentry/fs/tty/tty_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/hostcpu/getcpu_amd64.s b/pkg/sentry/hostcpu/getcpu_amd64.s index 7f6247d81..409db1450 100644 --- a/pkg/sentry/hostcpu/getcpu_amd64.s +++ b/pkg/sentry/hostcpu/getcpu_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/hostcpu/hostcpu.go b/pkg/sentry/hostcpu/hostcpu.go index fa46499ad..3adc847bb 100644 --- a/pkg/sentry/hostcpu/hostcpu.go +++ b/pkg/sentry/hostcpu/hostcpu.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/hostcpu/hostcpu_test.go b/pkg/sentry/hostcpu/hostcpu_test.go index a82e1a271..38de0e1f6 100644 --- a/pkg/sentry/hostcpu/hostcpu_test.go +++ b/pkg/sentry/hostcpu/hostcpu_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/inet/context.go b/pkg/sentry/inet/context.go index 370381f41..d05e96f15 100644 --- a/pkg/sentry/inet/context.go +++ b/pkg/sentry/inet/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/inet/inet.go b/pkg/sentry/inet/inet.go index 30ca4e0c0..8206377cc 100644 --- a/pkg/sentry/inet/inet.go +++ b/pkg/sentry/inet/inet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/inet/test_stack.go b/pkg/sentry/inet/test_stack.go index bc10926ee..05c1a1792 100644 --- a/pkg/sentry/inet/test_stack.go +++ b/pkg/sentry/inet/test_stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/abstract_socket_namespace.go b/pkg/sentry/kernel/abstract_socket_namespace.go index 45088c988..1ea2cee36 100644 --- a/pkg/sentry/kernel/abstract_socket_namespace.go +++ b/pkg/sentry/kernel/abstract_socket_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/auth.go b/pkg/sentry/kernel/auth/auth.go index c49a6b852..19f15fd36 100644 --- a/pkg/sentry/kernel/auth/auth.go +++ b/pkg/sentry/kernel/auth/auth.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/capability_set.go b/pkg/sentry/kernel/auth/capability_set.go index 5b8164c49..88d6243aa 100644 --- a/pkg/sentry/kernel/auth/capability_set.go +++ b/pkg/sentry/kernel/auth/capability_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/context.go b/pkg/sentry/kernel/auth/context.go index 914589b28..f7e945599 100644 --- a/pkg/sentry/kernel/auth/context.go +++ b/pkg/sentry/kernel/auth/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/credentials.go b/pkg/sentry/kernel/auth/credentials.go index f18f7dac9..de33f1953 100644 --- a/pkg/sentry/kernel/auth/credentials.go +++ b/pkg/sentry/kernel/auth/credentials.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/id.go b/pkg/sentry/kernel/auth/id.go index 37522b018..e5bed44d7 100644 --- a/pkg/sentry/kernel/auth/id.go +++ b/pkg/sentry/kernel/auth/id.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/id_map.go b/pkg/sentry/kernel/auth/id_map.go index bd0090e0f..43f439825 100644 --- a/pkg/sentry/kernel/auth/id_map.go +++ b/pkg/sentry/kernel/auth/id_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/id_map_functions.go b/pkg/sentry/kernel/auth/id_map_functions.go index 889291d96..8f1a189ec 100644 --- a/pkg/sentry/kernel/auth/id_map_functions.go +++ b/pkg/sentry/kernel/auth/id_map_functions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/auth/user_namespace.go b/pkg/sentry/kernel/auth/user_namespace.go index d359f3f31..5bb9c44c0 100644 --- a/pkg/sentry/kernel/auth/user_namespace.go +++ b/pkg/sentry/kernel/auth/user_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/context.go b/pkg/sentry/kernel/context.go index 261ca6f7a..b629521eb 100644 --- a/pkg/sentry/kernel/context.go +++ b/pkg/sentry/kernel/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/epoll/epoll.go b/pkg/sentry/kernel/epoll/epoll.go index a8eb114c0..9c13ecfcc 100644 --- a/pkg/sentry/kernel/epoll/epoll.go +++ b/pkg/sentry/kernel/epoll/epoll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/epoll/epoll_state.go b/pkg/sentry/kernel/epoll/epoll_state.go index dabb32f49..7f3e2004a 100644 --- a/pkg/sentry/kernel/epoll/epoll_state.go +++ b/pkg/sentry/kernel/epoll/epoll_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/epoll/epoll_test.go b/pkg/sentry/kernel/epoll/epoll_test.go index bc869fc13..d89c1b745 100644 --- a/pkg/sentry/kernel/epoll/epoll_test.go +++ b/pkg/sentry/kernel/epoll/epoll_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/eventfd/eventfd.go b/pkg/sentry/kernel/eventfd/eventfd.go index a4ada0e78..26dc59a85 100644 --- a/pkg/sentry/kernel/eventfd/eventfd.go +++ b/pkg/sentry/kernel/eventfd/eventfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/eventfd/eventfd_test.go b/pkg/sentry/kernel/eventfd/eventfd_test.go index 71326b62f..14e8996d9 100644 --- a/pkg/sentry/kernel/eventfd/eventfd_test.go +++ b/pkg/sentry/kernel/eventfd/eventfd_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/fasync/fasync.go b/pkg/sentry/kernel/fasync/fasync.go index f77339cae..aa4aac109 100644 --- a/pkg/sentry/kernel/fasync/fasync.go +++ b/pkg/sentry/kernel/fasync/fasync.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/fd_map.go b/pkg/sentry/kernel/fd_map.go index cad0b0a20..715f4714d 100644 --- a/pkg/sentry/kernel/fd_map.go +++ b/pkg/sentry/kernel/fd_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/fd_map_test.go b/pkg/sentry/kernel/fd_map_test.go index 95123aef3..b49996137 100644 --- a/pkg/sentry/kernel/fd_map_test.go +++ b/pkg/sentry/kernel/fd_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/fs_context.go b/pkg/sentry/kernel/fs_context.go index f3f05e8f5..3cf0db280 100644 --- a/pkg/sentry/kernel/fs_context.go +++ b/pkg/sentry/kernel/fs_context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/futex/futex.go b/pkg/sentry/kernel/futex/futex.go index 54b1982a0..ea69d433b 100644 --- a/pkg/sentry/kernel/futex/futex.go +++ b/pkg/sentry/kernel/futex/futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/futex/futex_test.go b/pkg/sentry/kernel/futex/futex_test.go index 726c26990..ea506a29b 100644 --- a/pkg/sentry/kernel/futex/futex_test.go +++ b/pkg/sentry/kernel/futex/futex_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/ipc_namespace.go b/pkg/sentry/kernel/ipc_namespace.go index 5eef49f59..9ceb9bd92 100644 --- a/pkg/sentry/kernel/ipc_namespace.go +++ b/pkg/sentry/kernel/ipc_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/kdefs/kdefs.go b/pkg/sentry/kernel/kdefs/kdefs.go index bbb476544..8eafe810b 100644 --- a/pkg/sentry/kernel/kdefs/kdefs.go +++ b/pkg/sentry/kernel/kdefs/kdefs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index 5d6856f3c..bad558d48 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/kernel_state.go b/pkg/sentry/kernel/kernel_state.go index bb2d5102d..a0a69b498 100644 --- a/pkg/sentry/kernel/kernel_state.go +++ b/pkg/sentry/kernel/kernel_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/memevent/memory_events.go b/pkg/sentry/kernel/memevent/memory_events.go index f7a183a1d..f05ef1b64 100644 --- a/pkg/sentry/kernel/memevent/memory_events.go +++ b/pkg/sentry/kernel/memevent/memory_events.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/memevent/memory_events.proto b/pkg/sentry/kernel/memevent/memory_events.proto index abc565054..43b8deb76 100644 --- a/pkg/sentry/kernel/memevent/memory_events.proto +++ b/pkg/sentry/kernel/memevent/memory_events.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pending_signals.go b/pkg/sentry/kernel/pending_signals.go index bb5db0309..373e11772 100644 --- a/pkg/sentry/kernel/pending_signals.go +++ b/pkg/sentry/kernel/pending_signals.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pending_signals_state.go b/pkg/sentry/kernel/pending_signals_state.go index 6d90ed033..72be6702f 100644 --- a/pkg/sentry/kernel/pending_signals_state.go +++ b/pkg/sentry/kernel/pending_signals_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/buffers.go b/pkg/sentry/kernel/pipe/buffers.go index a82e45c3f..fa8045910 100644 --- a/pkg/sentry/kernel/pipe/buffers.go +++ b/pkg/sentry/kernel/pipe/buffers.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/device.go b/pkg/sentry/kernel/pipe/device.go index 8d383577a..eec5c5de8 100644 --- a/pkg/sentry/kernel/pipe/device.go +++ b/pkg/sentry/kernel/pipe/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/node.go b/pkg/sentry/kernel/pipe/node.go index 23d692da1..4b0e00b85 100644 --- a/pkg/sentry/kernel/pipe/node.go +++ b/pkg/sentry/kernel/pipe/node.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/node_test.go b/pkg/sentry/kernel/pipe/node_test.go index cc1ebf4f6..eda551594 100644 --- a/pkg/sentry/kernel/pipe/node_test.go +++ b/pkg/sentry/kernel/pipe/node_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/pipe.go b/pkg/sentry/kernel/pipe/pipe.go index ced2559a7..126054826 100644 --- a/pkg/sentry/kernel/pipe/pipe.go +++ b/pkg/sentry/kernel/pipe/pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/pipe_test.go b/pkg/sentry/kernel/pipe/pipe_test.go index 49ef8c8ac..3b9895927 100644 --- a/pkg/sentry/kernel/pipe/pipe_test.go +++ b/pkg/sentry/kernel/pipe/pipe_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/reader.go b/pkg/sentry/kernel/pipe/reader.go index 1fa5e9a32..f27379969 100644 --- a/pkg/sentry/kernel/pipe/reader.go +++ b/pkg/sentry/kernel/pipe/reader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/reader_writer.go b/pkg/sentry/kernel/pipe/reader_writer.go index 82607367b..63efc5bbe 100644 --- a/pkg/sentry/kernel/pipe/reader_writer.go +++ b/pkg/sentry/kernel/pipe/reader_writer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/pipe/writer.go b/pkg/sentry/kernel/pipe/writer.go index d93324b53..6fea9769c 100644 --- a/pkg/sentry/kernel/pipe/writer.go +++ b/pkg/sentry/kernel/pipe/writer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/posixtimer.go b/pkg/sentry/kernel/posixtimer.go index 0ab958529..40b5acca3 100644 --- a/pkg/sentry/kernel/posixtimer.go +++ b/pkg/sentry/kernel/posixtimer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/ptrace.go b/pkg/sentry/kernel/ptrace.go index 9fe28f435..20bac2b70 100644 --- a/pkg/sentry/kernel/ptrace.go +++ b/pkg/sentry/kernel/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/rseq.go b/pkg/sentry/kernel/rseq.go index 1f3de58e3..46b03c700 100644 --- a/pkg/sentry/kernel/rseq.go +++ b/pkg/sentry/kernel/rseq.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/sched/cpuset.go b/pkg/sentry/kernel/sched/cpuset.go index 0a97603f0..69aee9127 100644 --- a/pkg/sentry/kernel/sched/cpuset.go +++ b/pkg/sentry/kernel/sched/cpuset.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/sched/cpuset_test.go b/pkg/sentry/kernel/sched/cpuset_test.go index 8a6e12958..a036ed513 100644 --- a/pkg/sentry/kernel/sched/cpuset_test.go +++ b/pkg/sentry/kernel/sched/cpuset_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/sched/sched.go b/pkg/sentry/kernel/sched/sched.go index f1de1da60..e59909baf 100644 --- a/pkg/sentry/kernel/sched/sched.go +++ b/pkg/sentry/kernel/sched/sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/seccomp.go b/pkg/sentry/kernel/seccomp.go index d77c05e2f..37dd3e4c9 100644 --- a/pkg/sentry/kernel/seccomp.go +++ b/pkg/sentry/kernel/seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/semaphore/semaphore.go b/pkg/sentry/kernel/semaphore/semaphore.go index aa07946cf..232a276dc 100644 --- a/pkg/sentry/kernel/semaphore/semaphore.go +++ b/pkg/sentry/kernel/semaphore/semaphore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/semaphore/semaphore_test.go b/pkg/sentry/kernel/semaphore/semaphore_test.go index f9eb382e9..5f886bf31 100644 --- a/pkg/sentry/kernel/semaphore/semaphore_test.go +++ b/pkg/sentry/kernel/semaphore/semaphore_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/sessions.go b/pkg/sentry/kernel/sessions.go index a9b4e7647..78a5b4063 100644 --- a/pkg/sentry/kernel/sessions.go +++ b/pkg/sentry/kernel/sessions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/shm/device.go b/pkg/sentry/kernel/shm/device.go index b0dacdbe0..bbc653ed8 100644 --- a/pkg/sentry/kernel/shm/device.go +++ b/pkg/sentry/kernel/shm/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/shm/shm.go b/pkg/sentry/kernel/shm/shm.go index 77973951e..8d0d14e45 100644 --- a/pkg/sentry/kernel/shm/shm.go +++ b/pkg/sentry/kernel/shm/shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/signal.go b/pkg/sentry/kernel/signal.go index e3a2a777a..b066df132 100644 --- a/pkg/sentry/kernel/signal.go +++ b/pkg/sentry/kernel/signal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/signal_handlers.go b/pkg/sentry/kernel/signal_handlers.go index 3649f5e4d..3f1ac9898 100644 --- a/pkg/sentry/kernel/signal_handlers.go +++ b/pkg/sentry/kernel/signal_handlers.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index 4c7811b6c..19b711e9c 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/syscalls_state.go b/pkg/sentry/kernel/syscalls_state.go index 826809a70..981455d46 100644 --- a/pkg/sentry/kernel/syscalls_state.go +++ b/pkg/sentry/kernel/syscalls_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/syslog.go b/pkg/sentry/kernel/syslog.go index 6531bd5d2..2aecf3eea 100644 --- a/pkg/sentry/kernel/syslog.go +++ b/pkg/sentry/kernel/syslog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/table_test.go b/pkg/sentry/kernel/table_test.go index 71ca75555..3b29d3c6a 100644 --- a/pkg/sentry/kernel/table_test.go +++ b/pkg/sentry/kernel/table_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go index 4f0b7fe3f..e22ec768d 100644 --- a/pkg/sentry/kernel/task.go +++ b/pkg/sentry/kernel/task.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_acct.go b/pkg/sentry/kernel/task_acct.go index d2052921e..24230af89 100644 --- a/pkg/sentry/kernel/task_acct.go +++ b/pkg/sentry/kernel/task_acct.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_block.go b/pkg/sentry/kernel/task_block.go index 6dc7b938e..e5027e551 100644 --- a/pkg/sentry/kernel/task_block.go +++ b/pkg/sentry/kernel/task_block.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_clone.go b/pkg/sentry/kernel/task_clone.go index de3aef40d..755fe0370 100644 --- a/pkg/sentry/kernel/task_clone.go +++ b/pkg/sentry/kernel/task_clone.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_context.go b/pkg/sentry/kernel/task_context.go index d2df7e9d1..45b8d2b04 100644 --- a/pkg/sentry/kernel/task_context.go +++ b/pkg/sentry/kernel/task_context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_exec.go b/pkg/sentry/kernel/task_exec.go index 1b760aba4..a9b74da8e 100644 --- a/pkg/sentry/kernel/task_exec.go +++ b/pkg/sentry/kernel/task_exec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_exit.go b/pkg/sentry/kernel/task_exit.go index 65969ca9b..44fbb487c 100644 --- a/pkg/sentry/kernel/task_exit.go +++ b/pkg/sentry/kernel/task_exit.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_futex.go b/pkg/sentry/kernel/task_futex.go index 62ebbcb0d..5a11ca3df 100644 --- a/pkg/sentry/kernel/task_futex.go +++ b/pkg/sentry/kernel/task_futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_identity.go b/pkg/sentry/kernel/task_identity.go index b0921b2eb..8f90ed786 100644 --- a/pkg/sentry/kernel/task_identity.go +++ b/pkg/sentry/kernel/task_identity.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_log.go b/pkg/sentry/kernel/task_log.go index 1769da210..f4c881c2d 100644 --- a/pkg/sentry/kernel/task_log.go +++ b/pkg/sentry/kernel/task_log.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_net.go b/pkg/sentry/kernel/task_net.go index 4df2e53d3..fc7cefc1f 100644 --- a/pkg/sentry/kernel/task_net.go +++ b/pkg/sentry/kernel/task_net.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_run.go b/pkg/sentry/kernel/task_run.go index 49ac933b7..596b9aa16 100644 --- a/pkg/sentry/kernel/task_run.go +++ b/pkg/sentry/kernel/task_run.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_sched.go b/pkg/sentry/kernel/task_sched.go index 19dcc963a..3b3cdc24a 100644 --- a/pkg/sentry/kernel/task_sched.go +++ b/pkg/sentry/kernel/task_sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_signals.go b/pkg/sentry/kernel/task_signals.go index e2925a708..fe24f7542 100644 --- a/pkg/sentry/kernel/task_signals.go +++ b/pkg/sentry/kernel/task_signals.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_start.go b/pkg/sentry/kernel/task_start.go index 6c8d7d316..c82a32c78 100644 --- a/pkg/sentry/kernel/task_start.go +++ b/pkg/sentry/kernel/task_start.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_stop.go b/pkg/sentry/kernel/task_stop.go index feaf6cae4..36846484c 100644 --- a/pkg/sentry/kernel/task_stop.go +++ b/pkg/sentry/kernel/task_stop.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_syscall.go b/pkg/sentry/kernel/task_syscall.go index f0373c375..0318adb35 100644 --- a/pkg/sentry/kernel/task_syscall.go +++ b/pkg/sentry/kernel/task_syscall.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_test.go b/pkg/sentry/kernel/task_test.go index 82ef858a1..3f37f505d 100644 --- a/pkg/sentry/kernel/task_test.go +++ b/pkg/sentry/kernel/task_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/task_usermem.go b/pkg/sentry/kernel/task_usermem.go index 2b4954869..c8e973bd5 100644 --- a/pkg/sentry/kernel/task_usermem.go +++ b/pkg/sentry/kernel/task_usermem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/thread_group.go b/pkg/sentry/kernel/thread_group.go index dfff7b52d..d7652f57c 100644 --- a/pkg/sentry/kernel/thread_group.go +++ b/pkg/sentry/kernel/thread_group.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/threads.go b/pkg/sentry/kernel/threads.go index 4e3d19e97..bdb907905 100644 --- a/pkg/sentry/kernel/threads.go +++ b/pkg/sentry/kernel/threads.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/time/context.go b/pkg/sentry/kernel/time/context.go index ac4dc01d8..3675ea20d 100644 --- a/pkg/sentry/kernel/time/context.go +++ b/pkg/sentry/kernel/time/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/time/time.go b/pkg/sentry/kernel/time/time.go index 52e0dfba1..ca0f4ba2e 100644 --- a/pkg/sentry/kernel/time/time.go +++ b/pkg/sentry/kernel/time/time.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/timekeeper.go b/pkg/sentry/kernel/timekeeper.go index 2167f3efe..6bff80f13 100644 --- a/pkg/sentry/kernel/timekeeper.go +++ b/pkg/sentry/kernel/timekeeper.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/timekeeper_state.go b/pkg/sentry/kernel/timekeeper_state.go index 2e7fed4d8..f3a3ed543 100644 --- a/pkg/sentry/kernel/timekeeper_state.go +++ b/pkg/sentry/kernel/timekeeper_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/timekeeper_test.go b/pkg/sentry/kernel/timekeeper_test.go index 34a5cec27..71674c21c 100644 --- a/pkg/sentry/kernel/timekeeper_test.go +++ b/pkg/sentry/kernel/timekeeper_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/uts_namespace.go b/pkg/sentry/kernel/uts_namespace.go index 7e0fe0d21..ed5f0c031 100644 --- a/pkg/sentry/kernel/uts_namespace.go +++ b/pkg/sentry/kernel/uts_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/vdso.go b/pkg/sentry/kernel/vdso.go index 971e8bc59..0ec858a4a 100644 --- a/pkg/sentry/kernel/vdso.go +++ b/pkg/sentry/kernel/vdso.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/kernel/version.go b/pkg/sentry/kernel/version.go index a9e84673f..72bb0f93c 100644 --- a/pkg/sentry/kernel/version.go +++ b/pkg/sentry/kernel/version.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/limits/context.go b/pkg/sentry/limits/context.go index 75e97bf92..bf413eb7d 100644 --- a/pkg/sentry/limits/context.go +++ b/pkg/sentry/limits/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/limits/limits.go b/pkg/sentry/limits/limits.go index 02c8b60e3..ba0b7d4fd 100644 --- a/pkg/sentry/limits/limits.go +++ b/pkg/sentry/limits/limits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/limits/limits_test.go b/pkg/sentry/limits/limits_test.go index dd6f80750..d41f62554 100644 --- a/pkg/sentry/limits/limits_test.go +++ b/pkg/sentry/limits/limits_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/limits/linux.go b/pkg/sentry/limits/linux.go index 8e6a24341..511db6733 100644 --- a/pkg/sentry/limits/linux.go +++ b/pkg/sentry/limits/linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/loader/elf.go b/pkg/sentry/loader/elf.go index 849be5a3d..9b1e81dc9 100644 --- a/pkg/sentry/loader/elf.go +++ b/pkg/sentry/loader/elf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/loader/interpreter.go b/pkg/sentry/loader/interpreter.go index 54534952b..06a3c7156 100644 --- a/pkg/sentry/loader/interpreter.go +++ b/pkg/sentry/loader/interpreter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/loader/loader.go b/pkg/sentry/loader/loader.go index 62b39e52b..d1417c4f1 100644 --- a/pkg/sentry/loader/loader.go +++ b/pkg/sentry/loader/loader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/loader/vdso.go b/pkg/sentry/loader/vdso.go index a06e27ac9..437cc5da1 100644 --- a/pkg/sentry/loader/vdso.go +++ b/pkg/sentry/loader/vdso.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/loader/vdso_state.go b/pkg/sentry/loader/vdso_state.go index dc71e1c2d..b327f0e1e 100644 --- a/pkg/sentry/loader/vdso_state.go +++ b/pkg/sentry/loader/vdso_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/memmap/mapping_set.go b/pkg/sentry/memmap/mapping_set.go index c9483905d..33cf16f91 100644 --- a/pkg/sentry/memmap/mapping_set.go +++ b/pkg/sentry/memmap/mapping_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/memmap/mapping_set_test.go b/pkg/sentry/memmap/mapping_set_test.go index 10668d404..49ee34548 100644 --- a/pkg/sentry/memmap/mapping_set_test.go +++ b/pkg/sentry/memmap/mapping_set_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/memmap/memmap.go b/pkg/sentry/memmap/memmap.go index cdc5f2b27..05349a77f 100644 --- a/pkg/sentry/memmap/memmap.go +++ b/pkg/sentry/memmap/memmap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/memutil/memutil.go b/pkg/sentry/memutil/memutil.go index 4f245cf3c..286d50ca4 100644 --- a/pkg/sentry/memutil/memutil.go +++ b/pkg/sentry/memutil/memutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/memutil/memutil_unsafe.go b/pkg/sentry/memutil/memutil_unsafe.go index 32c27eb2f..8d9fc64fb 100644 --- a/pkg/sentry/memutil/memutil_unsafe.go +++ b/pkg/sentry/memutil/memutil_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/address_space.go b/pkg/sentry/mm/address_space.go index 27554f163..7488f7c4a 100644 --- a/pkg/sentry/mm/address_space.go +++ b/pkg/sentry/mm/address_space.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/aio_context.go b/pkg/sentry/mm/aio_context.go index b42156d45..87942af0e 100644 --- a/pkg/sentry/mm/aio_context.go +++ b/pkg/sentry/mm/aio_context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/aio_context_state.go b/pkg/sentry/mm/aio_context_state.go index 1a5e56f8e..192a6f744 100644 --- a/pkg/sentry/mm/aio_context_state.go +++ b/pkg/sentry/mm/aio_context_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/debug.go b/pkg/sentry/mm/debug.go index 56d0490f0..d341b9c07 100644 --- a/pkg/sentry/mm/debug.go +++ b/pkg/sentry/mm/debug.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/io.go b/pkg/sentry/mm/io.go index 6741db594..6600ddd78 100644 --- a/pkg/sentry/mm/io.go +++ b/pkg/sentry/mm/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/lifecycle.go b/pkg/sentry/mm/lifecycle.go index a4b5cb443..b248b76e7 100644 --- a/pkg/sentry/mm/lifecycle.go +++ b/pkg/sentry/mm/lifecycle.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/metadata.go b/pkg/sentry/mm/metadata.go index 32d5e2ff6..5ef1ba0b1 100644 --- a/pkg/sentry/mm/metadata.go +++ b/pkg/sentry/mm/metadata.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/mm.go b/pkg/sentry/mm/mm.go index 3299ae164..aab697f9e 100644 --- a/pkg/sentry/mm/mm.go +++ b/pkg/sentry/mm/mm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/mm_test.go b/pkg/sentry/mm/mm_test.go index b47aa7263..f2db43196 100644 --- a/pkg/sentry/mm/mm_test.go +++ b/pkg/sentry/mm/mm_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/pma.go b/pkg/sentry/mm/pma.go index 9febb25ac..5690fe6b4 100644 --- a/pkg/sentry/mm/pma.go +++ b/pkg/sentry/mm/pma.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/proc_pid_maps.go b/pkg/sentry/mm/proc_pid_maps.go index 5840b257c..0bf1cdb51 100644 --- a/pkg/sentry/mm/proc_pid_maps.go +++ b/pkg/sentry/mm/proc_pid_maps.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/save_restore.go b/pkg/sentry/mm/save_restore.go index 36fed8f1c..6e7080a84 100644 --- a/pkg/sentry/mm/save_restore.go +++ b/pkg/sentry/mm/save_restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/shm.go b/pkg/sentry/mm/shm.go index bab137a5a..3bc48c7e7 100644 --- a/pkg/sentry/mm/shm.go +++ b/pkg/sentry/mm/shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/special_mappable.go b/pkg/sentry/mm/special_mappable.go index 5d7bd33bd..e511472f4 100644 --- a/pkg/sentry/mm/special_mappable.go +++ b/pkg/sentry/mm/special_mappable.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/syscalls.go b/pkg/sentry/mm/syscalls.go index b0622b0c3..a721cc456 100644 --- a/pkg/sentry/mm/syscalls.go +++ b/pkg/sentry/mm/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/mm/vma.go b/pkg/sentry/mm/vma.go index b81e861f1..dafdbd0e4 100644 --- a/pkg/sentry/mm/vma.go +++ b/pkg/sentry/mm/vma.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/context.go b/pkg/sentry/platform/context.go index 0d200a5e2..cca21a23e 100644 --- a/pkg/sentry/platform/context.go +++ b/pkg/sentry/platform/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/filemem/filemem.go b/pkg/sentry/platform/filemem/filemem.go index f278c8d63..97da31e70 100644 --- a/pkg/sentry/platform/filemem/filemem.go +++ b/pkg/sentry/platform/filemem/filemem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/filemem/filemem_state.go b/pkg/sentry/platform/filemem/filemem_state.go index e28e021c9..964e2aaaa 100644 --- a/pkg/sentry/platform/filemem/filemem_state.go +++ b/pkg/sentry/platform/filemem/filemem_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/filemem/filemem_test.go b/pkg/sentry/platform/filemem/filemem_test.go index 4b165dc48..9becec25f 100644 --- a/pkg/sentry/platform/filemem/filemem_test.go +++ b/pkg/sentry/platform/filemem/filemem_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/filemem/filemem_unsafe.go b/pkg/sentry/platform/filemem/filemem_unsafe.go index a23b9825a..776aed74d 100644 --- a/pkg/sentry/platform/filemem/filemem_unsafe.go +++ b/pkg/sentry/platform/filemem/filemem_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/interrupt/interrupt.go b/pkg/sentry/platform/interrupt/interrupt.go index ca4f42087..9c83f41eb 100644 --- a/pkg/sentry/platform/interrupt/interrupt.go +++ b/pkg/sentry/platform/interrupt/interrupt.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/interrupt/interrupt_test.go b/pkg/sentry/platform/interrupt/interrupt_test.go index 7c49eeea6..fb3284395 100644 --- a/pkg/sentry/platform/interrupt/interrupt_test.go +++ b/pkg/sentry/platform/interrupt/interrupt_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/address_space.go b/pkg/sentry/platform/kvm/address_space.go index c4293c517..72e897a9a 100644 --- a/pkg/sentry/platform/kvm/address_space.go +++ b/pkg/sentry/platform/kvm/address_space.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/allocator.go b/pkg/sentry/platform/kvm/allocator.go index f5cebd5b3..b25cad155 100644 --- a/pkg/sentry/platform/kvm/allocator.go +++ b/pkg/sentry/platform/kvm/allocator.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/bluepill.go b/pkg/sentry/platform/kvm/bluepill.go index ecc33d7dd..9f1c9510b 100644 --- a/pkg/sentry/platform/kvm/bluepill.go +++ b/pkg/sentry/platform/kvm/bluepill.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.go b/pkg/sentry/platform/kvm/bluepill_amd64.go index b364e3ef7..f013d1dc9 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64.go +++ b/pkg/sentry/platform/kvm/bluepill_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.s b/pkg/sentry/platform/kvm/bluepill_amd64.s index 0881bd5f5..ec017f6c2 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64.s +++ b/pkg/sentry/platform/kvm/bluepill_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go index 61ca61dcb..cd00a47f2 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/bluepill_fault.go b/pkg/sentry/platform/kvm/bluepill_fault.go index 8650cd78f..e79a30ef2 100644 --- a/pkg/sentry/platform/kvm/bluepill_fault.go +++ b/pkg/sentry/platform/kvm/bluepill_fault.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/bluepill_unsafe.go b/pkg/sentry/platform/kvm/bluepill_unsafe.go index 216d4b4b6..747a95997 100644 --- a/pkg/sentry/platform/kvm/bluepill_unsafe.go +++ b/pkg/sentry/platform/kvm/bluepill_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/context.go b/pkg/sentry/platform/kvm/context.go index aac84febf..be902be88 100644 --- a/pkg/sentry/platform/kvm/context.go +++ b/pkg/sentry/platform/kvm/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/host_map.go b/pkg/sentry/platform/kvm/host_map.go index fc16ad2de..ee6a1a42d 100644 --- a/pkg/sentry/platform/kvm/host_map.go +++ b/pkg/sentry/platform/kvm/host_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/kvm.go b/pkg/sentry/platform/kvm/kvm.go index 0c4dff308..d4f50024d 100644 --- a/pkg/sentry/platform/kvm/kvm.go +++ b/pkg/sentry/platform/kvm/kvm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/kvm_amd64.go b/pkg/sentry/platform/kvm/kvm_amd64.go index 3d56ed895..70d0ac63b 100644 --- a/pkg/sentry/platform/kvm/kvm_amd64.go +++ b/pkg/sentry/platform/kvm/kvm_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go b/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go index 476e783a0..c0a0af92d 100644 --- a/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/kvm_const.go b/pkg/sentry/platform/kvm/kvm_const.go index ca44c31b3..8c53c6f06 100644 --- a/pkg/sentry/platform/kvm/kvm_const.go +++ b/pkg/sentry/platform/kvm/kvm_const.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/kvm_test.go b/pkg/sentry/platform/kvm/kvm_test.go index 52448839f..45eeb96ff 100644 --- a/pkg/sentry/platform/kvm/kvm_test.go +++ b/pkg/sentry/platform/kvm/kvm_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/machine.go b/pkg/sentry/platform/kvm/machine.go index 9f60b6b31..fc7ad258f 100644 --- a/pkg/sentry/platform/kvm/machine.go +++ b/pkg/sentry/platform/kvm/machine.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/machine_amd64.go b/pkg/sentry/platform/kvm/machine_amd64.go index bcd29a947..e0aec42b8 100644 --- a/pkg/sentry/platform/kvm/machine_amd64.go +++ b/pkg/sentry/platform/kvm/machine_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go index 8b9041f13..50e513f3b 100644 --- a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/machine_unsafe.go b/pkg/sentry/platform/kvm/machine_unsafe.go index 86323c891..4f5b01321 100644 --- a/pkg/sentry/platform/kvm/machine_unsafe.go +++ b/pkg/sentry/platform/kvm/machine_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/physical_map.go b/pkg/sentry/platform/kvm/physical_map.go index 81a98656d..b908cae6a 100644 --- a/pkg/sentry/platform/kvm/physical_map.go +++ b/pkg/sentry/platform/kvm/physical_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/testutil/testutil.go b/pkg/sentry/platform/kvm/testutil/testutil.go index 8a614e25d..0d496561d 100644 --- a/pkg/sentry/platform/kvm/testutil/testutil.go +++ b/pkg/sentry/platform/kvm/testutil/testutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/testutil/testutil_amd64.go b/pkg/sentry/platform/kvm/testutil/testutil_amd64.go index 39286a0af..fcba33813 100644 --- a/pkg/sentry/platform/kvm/testutil/testutil_amd64.go +++ b/pkg/sentry/platform/kvm/testutil/testutil_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/testutil/testutil_amd64.s b/pkg/sentry/platform/kvm/testutil/testutil_amd64.s index 3b5ad8817..f1da41a44 100644 --- a/pkg/sentry/platform/kvm/testutil/testutil_amd64.s +++ b/pkg/sentry/platform/kvm/testutil/testutil_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/virtual_map.go b/pkg/sentry/platform/kvm/virtual_map.go index 0d3fbe043..0343e9267 100644 --- a/pkg/sentry/platform/kvm/virtual_map.go +++ b/pkg/sentry/platform/kvm/virtual_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/kvm/virtual_map_test.go b/pkg/sentry/platform/kvm/virtual_map_test.go index 7875bd3e9..935e0eb93 100644 --- a/pkg/sentry/platform/kvm/virtual_map_test.go +++ b/pkg/sentry/platform/kvm/virtual_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/mmap_min_addr.go b/pkg/sentry/platform/mmap_min_addr.go index 6398e5e01..1bcc1f8e9 100644 --- a/pkg/sentry/platform/mmap_min_addr.go +++ b/pkg/sentry/platform/mmap_min_addr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/platform.go b/pkg/sentry/platform/platform.go index 8a1620d93..f16588e6e 100644 --- a/pkg/sentry/platform/platform.go +++ b/pkg/sentry/platform/platform.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/procid/procid.go b/pkg/sentry/platform/procid/procid.go index 5f861908f..3f49ab093 100644 --- a/pkg/sentry/platform/procid/procid.go +++ b/pkg/sentry/platform/procid/procid.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/procid/procid_amd64.s b/pkg/sentry/platform/procid/procid_amd64.s index 5b1ba1f24..fd88ce82e 100644 --- a/pkg/sentry/platform/procid/procid_amd64.s +++ b/pkg/sentry/platform/procid/procid_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/procid/procid_net_test.go b/pkg/sentry/platform/procid/procid_net_test.go index 2d1605a08..e8dcc479d 100644 --- a/pkg/sentry/platform/procid/procid_net_test.go +++ b/pkg/sentry/platform/procid/procid_net_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/procid/procid_test.go b/pkg/sentry/platform/procid/procid_test.go index 5e44da36f..7a57c7cdc 100644 --- a/pkg/sentry/platform/procid/procid_test.go +++ b/pkg/sentry/platform/procid/procid_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/ptrace.go b/pkg/sentry/platform/ptrace/ptrace.go index 4f20716f7..00d92b092 100644 --- a/pkg/sentry/platform/ptrace/ptrace.go +++ b/pkg/sentry/platform/ptrace/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/ptrace_unsafe.go b/pkg/sentry/platform/ptrace/ptrace_unsafe.go index 46a8bda8e..7a3cb8f49 100644 --- a/pkg/sentry/platform/ptrace/ptrace_unsafe.go +++ b/pkg/sentry/platform/ptrace/ptrace_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/stub_amd64.s b/pkg/sentry/platform/ptrace/stub_amd64.s index 9bf87b6f6..63f98e40d 100644 --- a/pkg/sentry/platform/ptrace/stub_amd64.s +++ b/pkg/sentry/platform/ptrace/stub_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/stub_unsafe.go b/pkg/sentry/platform/ptrace/stub_unsafe.go index c868a2d68..48c16c4a1 100644 --- a/pkg/sentry/platform/ptrace/stub_unsafe.go +++ b/pkg/sentry/platform/ptrace/stub_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/subprocess.go b/pkg/sentry/platform/ptrace/subprocess.go index 6d5ad6b71..6a9da5db8 100644 --- a/pkg/sentry/platform/ptrace/subprocess.go +++ b/pkg/sentry/platform/ptrace/subprocess.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/subprocess_amd64.go b/pkg/sentry/platform/ptrace/subprocess_amd64.go index c38dc1ff8..d23a1133e 100644 --- a/pkg/sentry/platform/ptrace/subprocess_amd64.go +++ b/pkg/sentry/platform/ptrace/subprocess_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/subprocess_linux.go b/pkg/sentry/platform/ptrace/subprocess_linux.go index 53adadadd..7523487e7 100644 --- a/pkg/sentry/platform/ptrace/subprocess_linux.go +++ b/pkg/sentry/platform/ptrace/subprocess_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go b/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go index 697431472..0c9263060 100644 --- a/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go +++ b/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ptrace/subprocess_unsafe.go b/pkg/sentry/platform/ptrace/subprocess_unsafe.go index fe41641d3..ca6c4ac97 100644 --- a/pkg/sentry/platform/ptrace/subprocess_unsafe.go +++ b/pkg/sentry/platform/ptrace/subprocess_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/defs.go b/pkg/sentry/platform/ring0/defs.go index f09d045eb..18137e55d 100644 --- a/pkg/sentry/platform/ring0/defs.go +++ b/pkg/sentry/platform/ring0/defs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/defs_amd64.go b/pkg/sentry/platform/ring0/defs_amd64.go index 84819f132..67242b92b 100644 --- a/pkg/sentry/platform/ring0/defs_amd64.go +++ b/pkg/sentry/platform/ring0/defs_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/entry_amd64.go b/pkg/sentry/platform/ring0/entry_amd64.go index a3e992e0d..4a9affe64 100644 --- a/pkg/sentry/platform/ring0/entry_amd64.go +++ b/pkg/sentry/platform/ring0/entry_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/entry_amd64.s b/pkg/sentry/platform/ring0/entry_amd64.s index 08c15ad65..d48fbd2d1 100644 --- a/pkg/sentry/platform/ring0/entry_amd64.s +++ b/pkg/sentry/platform/ring0/entry_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/gen_offsets/main.go b/pkg/sentry/platform/ring0/gen_offsets/main.go index ffa7eaf77..11c49855f 100644 --- a/pkg/sentry/platform/ring0/gen_offsets/main.go +++ b/pkg/sentry/platform/ring0/gen_offsets/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/kernel.go b/pkg/sentry/platform/ring0/kernel.go index 62e67005e..e70eafde2 100644 --- a/pkg/sentry/platform/ring0/kernel.go +++ b/pkg/sentry/platform/ring0/kernel.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/kernel_amd64.go b/pkg/sentry/platform/ring0/kernel_amd64.go index 0d2b0f7dc..ab562bca7 100644 --- a/pkg/sentry/platform/ring0/kernel_amd64.go +++ b/pkg/sentry/platform/ring0/kernel_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/kernel_unsafe.go b/pkg/sentry/platform/ring0/kernel_unsafe.go index cfb3ad853..faf4240e5 100644 --- a/pkg/sentry/platform/ring0/kernel_unsafe.go +++ b/pkg/sentry/platform/ring0/kernel_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/lib_amd64.go b/pkg/sentry/platform/ring0/lib_amd64.go index 989e3e383..2b95a0141 100644 --- a/pkg/sentry/platform/ring0/lib_amd64.go +++ b/pkg/sentry/platform/ring0/lib_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/lib_amd64.s b/pkg/sentry/platform/ring0/lib_amd64.s index 6f143ea5a..98a130525 100644 --- a/pkg/sentry/platform/ring0/lib_amd64.s +++ b/pkg/sentry/platform/ring0/lib_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/offsets_amd64.go b/pkg/sentry/platform/ring0/offsets_amd64.go index ca5fd456b..753d31ef8 100644 --- a/pkg/sentry/platform/ring0/offsets_amd64.go +++ b/pkg/sentry/platform/ring0/offsets_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/allocator.go b/pkg/sentry/platform/ring0/pagetables/allocator.go index 049fd0247..ee6e90a11 100644 --- a/pkg/sentry/platform/ring0/pagetables/allocator.go +++ b/pkg/sentry/platform/ring0/pagetables/allocator.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go b/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go index aca778913..f48647b3a 100644 --- a/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go +++ b/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables.go b/pkg/sentry/platform/ring0/pagetables/pagetables.go index ff5787f89..c7207ec18 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go index 878463018..746f614e5 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go index a7f2ad9a4..2f82c4353 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_test.go b/pkg/sentry/platform/ring0/pagetables/pagetables_test.go index dca3f69ef..3e5dc7dc7 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_test.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go b/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go index ca49d20f8..6bd8c3584 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/pcids_x86.go b/pkg/sentry/platform/ring0/pagetables/pcids_x86.go index fa068e35e..0d9a51aa5 100644 --- a/pkg/sentry/platform/ring0/pagetables/pcids_x86.go +++ b/pkg/sentry/platform/ring0/pagetables/pcids_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/pagetables/walker_amd64.go b/pkg/sentry/platform/ring0/pagetables/walker_amd64.go index afa4d473a..c4c71d23e 100644 --- a/pkg/sentry/platform/ring0/pagetables/walker_amd64.go +++ b/pkg/sentry/platform/ring0/pagetables/walker_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/ring0.go b/pkg/sentry/platform/ring0/ring0.go index 4991031c5..10c51e88d 100644 --- a/pkg/sentry/platform/ring0/ring0.go +++ b/pkg/sentry/platform/ring0/ring0.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/ring0/x86.go b/pkg/sentry/platform/ring0/x86.go index f489fcecb..7c88010d8 100644 --- a/pkg/sentry/platform/ring0/x86.go +++ b/pkg/sentry/platform/ring0/x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/safecopy/atomic_amd64.s b/pkg/sentry/platform/safecopy/atomic_amd64.s index 69947dec3..873ffa046 100644 --- a/pkg/sentry/platform/safecopy/atomic_amd64.s +++ b/pkg/sentry/platform/safecopy/atomic_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/safecopy/memclr_amd64.s b/pkg/sentry/platform/safecopy/memclr_amd64.s index 7d1019f60..488b6e666 100644 --- a/pkg/sentry/platform/safecopy/memclr_amd64.s +++ b/pkg/sentry/platform/safecopy/memclr_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/safecopy/memcpy_amd64.s b/pkg/sentry/platform/safecopy/memcpy_amd64.s index 96ef2eefc..0bf26fd7b 100644 --- a/pkg/sentry/platform/safecopy/memcpy_amd64.s +++ b/pkg/sentry/platform/safecopy/memcpy_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/safecopy/safecopy.go b/pkg/sentry/platform/safecopy/safecopy.go index 90a2aad7b..c60f73103 100644 --- a/pkg/sentry/platform/safecopy/safecopy.go +++ b/pkg/sentry/platform/safecopy/safecopy.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/safecopy/safecopy_test.go b/pkg/sentry/platform/safecopy/safecopy_test.go index 67df36121..1a682d28a 100644 --- a/pkg/sentry/platform/safecopy/safecopy_test.go +++ b/pkg/sentry/platform/safecopy/safecopy_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/safecopy/safecopy_unsafe.go b/pkg/sentry/platform/safecopy/safecopy_unsafe.go index 72f243f8d..df1c35b66 100644 --- a/pkg/sentry/platform/safecopy/safecopy_unsafe.go +++ b/pkg/sentry/platform/safecopy/safecopy_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/platform/safecopy/sighandler_amd64.s b/pkg/sentry/platform/safecopy/sighandler_amd64.s index a65cb0c26..06614f1b4 100644 --- a/pkg/sentry/platform/safecopy/sighandler_amd64.s +++ b/pkg/sentry/platform/safecopy/sighandler_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/safemem/block_unsafe.go b/pkg/sentry/safemem/block_unsafe.go index 0b58f6497..e91ff66ae 100644 --- a/pkg/sentry/safemem/block_unsafe.go +++ b/pkg/sentry/safemem/block_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/safemem/io.go b/pkg/sentry/safemem/io.go index fd917648b..6cb52439f 100644 --- a/pkg/sentry/safemem/io.go +++ b/pkg/sentry/safemem/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/safemem/io_test.go b/pkg/sentry/safemem/io_test.go index edac4c1d7..2eda8c3bb 100644 --- a/pkg/sentry/safemem/io_test.go +++ b/pkg/sentry/safemem/io_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/safemem/safemem.go b/pkg/sentry/safemem/safemem.go index 2f8002004..090932d3e 100644 --- a/pkg/sentry/safemem/safemem.go +++ b/pkg/sentry/safemem/safemem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/safemem/seq_test.go b/pkg/sentry/safemem/seq_test.go index 3e83b3851..fddcaf714 100644 --- a/pkg/sentry/safemem/seq_test.go +++ b/pkg/sentry/safemem/seq_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/safemem/seq_unsafe.go b/pkg/sentry/safemem/seq_unsafe.go index e0d29a0b3..83a6b7183 100644 --- a/pkg/sentry/safemem/seq_unsafe.go +++ b/pkg/sentry/safemem/seq_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/sighandling/sighandling.go b/pkg/sentry/sighandling/sighandling.go index 29bcf55ab..6b5d5f993 100644 --- a/pkg/sentry/sighandling/sighandling.go +++ b/pkg/sentry/sighandling/sighandling.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/sighandling/sighandling_unsafe.go b/pkg/sentry/sighandling/sighandling_unsafe.go index a455b919f..5913d47a8 100644 --- a/pkg/sentry/sighandling/sighandling_unsafe.go +++ b/pkg/sentry/sighandling/sighandling_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/control/control.go b/pkg/sentry/socket/control/control.go index db97e95f2..d44f5e88a 100644 --- a/pkg/sentry/socket/control/control.go +++ b/pkg/sentry/socket/control/control.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/epsocket/device.go b/pkg/sentry/socket/epsocket/device.go index 17f2c9559..3cc138eb0 100644 --- a/pkg/sentry/socket/epsocket/device.go +++ b/pkg/sentry/socket/epsocket/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go index 47c575e7b..e90ef4835 100644 --- a/pkg/sentry/socket/epsocket/epsocket.go +++ b/pkg/sentry/socket/epsocket/epsocket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/epsocket/provider.go b/pkg/sentry/socket/epsocket/provider.go index dbc232d26..686554437 100644 --- a/pkg/sentry/socket/epsocket/provider.go +++ b/pkg/sentry/socket/epsocket/provider.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/epsocket/save_restore.go b/pkg/sentry/socket/epsocket/save_restore.go index 2613f90de..34d9a7cf0 100644 --- a/pkg/sentry/socket/epsocket/save_restore.go +++ b/pkg/sentry/socket/epsocket/save_restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/epsocket/stack.go b/pkg/sentry/socket/epsocket/stack.go index e4ed52fc8..c0081c819 100644 --- a/pkg/sentry/socket/epsocket/stack.go +++ b/pkg/sentry/socket/epsocket/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/hostinet/device.go b/pkg/sentry/socket/hostinet/device.go index a9a673316..c5133f3bb 100644 --- a/pkg/sentry/socket/hostinet/device.go +++ b/pkg/sentry/socket/hostinet/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/hostinet/hostinet.go b/pkg/sentry/socket/hostinet/hostinet.go index 67c6c8066..7858892ab 100644 --- a/pkg/sentry/socket/hostinet/hostinet.go +++ b/pkg/sentry/socket/hostinet/hostinet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/hostinet/save_restore.go b/pkg/sentry/socket/hostinet/save_restore.go index 0821a794a..3827f082a 100644 --- a/pkg/sentry/socket/hostinet/save_restore.go +++ b/pkg/sentry/socket/hostinet/save_restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go index e82624b44..e4e950fbb 100644 --- a/pkg/sentry/socket/hostinet/socket.go +++ b/pkg/sentry/socket/hostinet/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/hostinet/socket_unsafe.go b/pkg/sentry/socket/hostinet/socket_unsafe.go index f8bb75636..59c8910ca 100644 --- a/pkg/sentry/socket/hostinet/socket_unsafe.go +++ b/pkg/sentry/socket/hostinet/socket_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go index f64809d39..4ce73c1f1 100644 --- a/pkg/sentry/socket/hostinet/stack.go +++ b/pkg/sentry/socket/hostinet/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/netlink/message.go b/pkg/sentry/socket/netlink/message.go index b902d7ec9..a95172cba 100644 --- a/pkg/sentry/socket/netlink/message.go +++ b/pkg/sentry/socket/netlink/message.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/netlink/port/port.go b/pkg/sentry/socket/netlink/port/port.go index 1c5d4c3a5..20b9a6e37 100644 --- a/pkg/sentry/socket/netlink/port/port.go +++ b/pkg/sentry/socket/netlink/port/port.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/netlink/port/port_test.go b/pkg/sentry/socket/netlink/port/port_test.go index 34565e2f9..49b3b48ab 100644 --- a/pkg/sentry/socket/netlink/port/port_test.go +++ b/pkg/sentry/socket/netlink/port/port_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/netlink/provider.go b/pkg/sentry/socket/netlink/provider.go index 5d0a04a07..06786bd50 100644 --- a/pkg/sentry/socket/netlink/provider.go +++ b/pkg/sentry/socket/netlink/provider.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index 70322b9ed..7e70b09b2 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go index 0c03997f2..4d4130a4c 100644 --- a/pkg/sentry/socket/netlink/socket.go +++ b/pkg/sentry/socket/netlink/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/rpcinet/conn/conn.go b/pkg/sentry/socket/rpcinet/conn/conn.go index f4c8489b1..9c749b888 100644 --- a/pkg/sentry/socket/rpcinet/conn/conn.go +++ b/pkg/sentry/socket/rpcinet/conn/conn.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/rpcinet/device.go b/pkg/sentry/socket/rpcinet/device.go index f7b63436e..d2b9f9222 100644 --- a/pkg/sentry/socket/rpcinet/device.go +++ b/pkg/sentry/socket/rpcinet/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/rpcinet/notifier/notifier.go b/pkg/sentry/socket/rpcinet/notifier/notifier.go index f88a908ed..73c255c33 100644 --- a/pkg/sentry/socket/rpcinet/notifier/notifier.go +++ b/pkg/sentry/socket/rpcinet/notifier/notifier.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/rpcinet/rpcinet.go b/pkg/sentry/socket/rpcinet/rpcinet.go index 10b0dedc2..6c98e6acb 100644 --- a/pkg/sentry/socket/rpcinet/rpcinet.go +++ b/pkg/sentry/socket/rpcinet/rpcinet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/rpcinet/socket.go b/pkg/sentry/socket/rpcinet/socket.go index c7e761d54..44fa5c620 100644 --- a/pkg/sentry/socket/rpcinet/socket.go +++ b/pkg/sentry/socket/rpcinet/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/rpcinet/stack.go b/pkg/sentry/socket/rpcinet/stack.go index bcb89fb34..cb8344ec6 100644 --- a/pkg/sentry/socket/rpcinet/stack.go +++ b/pkg/sentry/socket/rpcinet/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/rpcinet/stack_unsafe.go b/pkg/sentry/socket/rpcinet/stack_unsafe.go index 9a896c623..d04fb2069 100644 --- a/pkg/sentry/socket/rpcinet/stack_unsafe.go +++ b/pkg/sentry/socket/rpcinet/stack_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/socket.go b/pkg/sentry/socket/socket.go index 31f8d42d7..a235c5249 100644 --- a/pkg/sentry/socket/socket.go +++ b/pkg/sentry/socket/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/device.go b/pkg/sentry/socket/unix/device.go index e8bcc7a9f..41820dbb3 100644 --- a/pkg/sentry/socket/unix/device.go +++ b/pkg/sentry/socket/unix/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/io.go b/pkg/sentry/socket/unix/io.go index 06333e14b..7d6434696 100644 --- a/pkg/sentry/socket/unix/io.go +++ b/pkg/sentry/socket/unix/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/transport/connectioned.go b/pkg/sentry/socket/unix/transport/connectioned.go index 566e3d57b..4c913effc 100644 --- a/pkg/sentry/socket/unix/transport/connectioned.go +++ b/pkg/sentry/socket/unix/transport/connectioned.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/transport/connectioned_state.go b/pkg/sentry/socket/unix/transport/connectioned_state.go index 7e6c73dcc..608a6a97a 100644 --- a/pkg/sentry/socket/unix/transport/connectioned_state.go +++ b/pkg/sentry/socket/unix/transport/connectioned_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/transport/connectionless.go b/pkg/sentry/socket/unix/transport/connectionless.go index 86cd05199..cd4633106 100644 --- a/pkg/sentry/socket/unix/transport/connectionless.go +++ b/pkg/sentry/socket/unix/transport/connectionless.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/transport/queue.go b/pkg/sentry/socket/unix/transport/queue.go index c4d7d863c..5b4dfab68 100644 --- a/pkg/sentry/socket/unix/transport/queue.go +++ b/pkg/sentry/socket/unix/transport/queue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/transport/unix.go b/pkg/sentry/socket/unix/transport/unix.go index 2934101a2..157133b65 100644 --- a/pkg/sentry/socket/unix/transport/unix.go +++ b/pkg/sentry/socket/unix/transport/unix.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/socket/unix/unix.go b/pkg/sentry/socket/unix/unix.go index 668363864..3543dd81f 100644 --- a/pkg/sentry/socket/unix/unix.go +++ b/pkg/sentry/socket/unix/unix.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/state/state.go b/pkg/sentry/state/state.go index 43e88a713..70b33f190 100644 --- a/pkg/sentry/state/state.go +++ b/pkg/sentry/state/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/state/state_metadata.go b/pkg/sentry/state/state_metadata.go index afa21672a..7f047b808 100644 --- a/pkg/sentry/state/state_metadata.go +++ b/pkg/sentry/state/state_metadata.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/state/state_unsafe.go b/pkg/sentry/state/state_unsafe.go index 3ff7d24c8..f02e12b2a 100644 --- a/pkg/sentry/state/state_unsafe.go +++ b/pkg/sentry/state/state_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/clone.go b/pkg/sentry/strace/clone.go index b82ca1ad1..e18ce84dc 100644 --- a/pkg/sentry/strace/clone.go +++ b/pkg/sentry/strace/clone.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/futex.go b/pkg/sentry/strace/futex.go index 3da108cb7..ceb3dc21d 100644 --- a/pkg/sentry/strace/futex.go +++ b/pkg/sentry/strace/futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/linux64.go b/pkg/sentry/strace/linux64.go index 1df148e7d..99714f12c 100644 --- a/pkg/sentry/strace/linux64.go +++ b/pkg/sentry/strace/linux64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/open.go b/pkg/sentry/strace/open.go index 839d5eda7..5a72a940c 100644 --- a/pkg/sentry/strace/open.go +++ b/pkg/sentry/strace/open.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/ptrace.go b/pkg/sentry/strace/ptrace.go index fcdb7e9f4..c572aafb4 100644 --- a/pkg/sentry/strace/ptrace.go +++ b/pkg/sentry/strace/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/socket.go b/pkg/sentry/strace/socket.go index 26831edd6..375418dc1 100644 --- a/pkg/sentry/strace/socket.go +++ b/pkg/sentry/strace/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/strace.go b/pkg/sentry/strace/strace.go index f7bfa3a1f..4286f0df7 100644 --- a/pkg/sentry/strace/strace.go +++ b/pkg/sentry/strace/strace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/strace.proto b/pkg/sentry/strace/strace.proto index 914e8c7b0..f1fc539d6 100644 --- a/pkg/sentry/strace/strace.proto +++ b/pkg/sentry/strace/strace.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/strace/syscalls.go b/pkg/sentry/strace/syscalls.go index 8be4fa318..9eeb18a03 100644 --- a/pkg/sentry/strace/syscalls.go +++ b/pkg/sentry/strace/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/epoll.go b/pkg/sentry/syscalls/epoll.go index 01dd6fa71..b90d191b7 100644 --- a/pkg/sentry/syscalls/epoll.go +++ b/pkg/sentry/syscalls/epoll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/error.go b/pkg/sentry/syscalls/linux/error.go index 013b385bc..9fd002955 100644 --- a/pkg/sentry/syscalls/linux/error.go +++ b/pkg/sentry/syscalls/linux/error.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/flags.go b/pkg/sentry/syscalls/linux/flags.go index f01483cd3..d1e0833fc 100644 --- a/pkg/sentry/syscalls/linux/flags.go +++ b/pkg/sentry/syscalls/linux/flags.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index 4465549ad..75e87f5ec 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sigset.go b/pkg/sentry/syscalls/linux/sigset.go index bfb541634..a033b7c70 100644 --- a/pkg/sentry/syscalls/linux/sigset.go +++ b/pkg/sentry/syscalls/linux/sigset.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_aio.go b/pkg/sentry/syscalls/linux/sys_aio.go index 54e4afa9e..355071131 100644 --- a/pkg/sentry/syscalls/linux/sys_aio.go +++ b/pkg/sentry/syscalls/linux/sys_aio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_capability.go b/pkg/sentry/syscalls/linux/sys_capability.go index 89c81ac90..cf972dc28 100644 --- a/pkg/sentry/syscalls/linux/sys_capability.go +++ b/pkg/sentry/syscalls/linux/sys_capability.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_epoll.go b/pkg/sentry/syscalls/linux/sys_epoll.go index e69dfc77a..62272efcd 100644 --- a/pkg/sentry/syscalls/linux/sys_epoll.go +++ b/pkg/sentry/syscalls/linux/sys_epoll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_eventfd.go b/pkg/sentry/syscalls/linux/sys_eventfd.go index 60fe5a133..903172890 100644 --- a/pkg/sentry/syscalls/linux/sys_eventfd.go +++ b/pkg/sentry/syscalls/linux/sys_eventfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_file.go b/pkg/sentry/syscalls/linux/sys_file.go index 64704bb88..a70f35be0 100644 --- a/pkg/sentry/syscalls/linux/sys_file.go +++ b/pkg/sentry/syscalls/linux/sys_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_futex.go b/pkg/sentry/syscalls/linux/sys_futex.go index d35dcecbe..cf04428bc 100644 --- a/pkg/sentry/syscalls/linux/sys_futex.go +++ b/pkg/sentry/syscalls/linux/sys_futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_getdents.go b/pkg/sentry/syscalls/linux/sys_getdents.go index 29c0d7a39..4b441b31b 100644 --- a/pkg/sentry/syscalls/linux/sys_getdents.go +++ b/pkg/sentry/syscalls/linux/sys_getdents.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_identity.go b/pkg/sentry/syscalls/linux/sys_identity.go index 4fd0ed794..8d594aa83 100644 --- a/pkg/sentry/syscalls/linux/sys_identity.go +++ b/pkg/sentry/syscalls/linux/sys_identity.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_inotify.go b/pkg/sentry/syscalls/linux/sys_inotify.go index 725204dff..26a505782 100644 --- a/pkg/sentry/syscalls/linux/sys_inotify.go +++ b/pkg/sentry/syscalls/linux/sys_inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_lseek.go b/pkg/sentry/syscalls/linux/sys_lseek.go index 97b51ba7c..ad3bfd761 100644 --- a/pkg/sentry/syscalls/linux/sys_lseek.go +++ b/pkg/sentry/syscalls/linux/sys_lseek.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_mmap.go b/pkg/sentry/syscalls/linux/sys_mmap.go index 1a98328dc..f8d9c43fd 100644 --- a/pkg/sentry/syscalls/linux/sys_mmap.go +++ b/pkg/sentry/syscalls/linux/sys_mmap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_mount.go b/pkg/sentry/syscalls/linux/sys_mount.go index 57cedccc1..bf0df7302 100644 --- a/pkg/sentry/syscalls/linux/sys_mount.go +++ b/pkg/sentry/syscalls/linux/sys_mount.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_pipe.go b/pkg/sentry/syscalls/linux/sys_pipe.go index 2b544f145..3652c429e 100644 --- a/pkg/sentry/syscalls/linux/sys_pipe.go +++ b/pkg/sentry/syscalls/linux/sys_pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_poll.go b/pkg/sentry/syscalls/linux/sys_poll.go index b9bdefadb..bf0958435 100644 --- a/pkg/sentry/syscalls/linux/sys_poll.go +++ b/pkg/sentry/syscalls/linux/sys_poll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_prctl.go b/pkg/sentry/syscalls/linux/sys_prctl.go index a1242acd3..c7b39ede8 100644 --- a/pkg/sentry/syscalls/linux/sys_prctl.go +++ b/pkg/sentry/syscalls/linux/sys_prctl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_random.go b/pkg/sentry/syscalls/linux/sys_random.go index be31e6b17..452dff058 100644 --- a/pkg/sentry/syscalls/linux/sys_random.go +++ b/pkg/sentry/syscalls/linux/sys_random.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_read.go b/pkg/sentry/syscalls/linux/sys_read.go index 0be2d195a..b2e5a5449 100644 --- a/pkg/sentry/syscalls/linux/sys_read.go +++ b/pkg/sentry/syscalls/linux/sys_read.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_rlimit.go b/pkg/sentry/syscalls/linux/sys_rlimit.go index d806b58ab..2f16e1791 100644 --- a/pkg/sentry/syscalls/linux/sys_rlimit.go +++ b/pkg/sentry/syscalls/linux/sys_rlimit.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_rusage.go b/pkg/sentry/syscalls/linux/sys_rusage.go index 82e42b589..ab07c77f9 100644 --- a/pkg/sentry/syscalls/linux/sys_rusage.go +++ b/pkg/sentry/syscalls/linux/sys_rusage.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_sched.go b/pkg/sentry/syscalls/linux/sys_sched.go index ff9e46077..e679a6694 100644 --- a/pkg/sentry/syscalls/linux/sys_sched.go +++ b/pkg/sentry/syscalls/linux/sys_sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_seccomp.go b/pkg/sentry/syscalls/linux/sys_seccomp.go index 4323a4df4..969acaa36 100644 --- a/pkg/sentry/syscalls/linux/sys_seccomp.go +++ b/pkg/sentry/syscalls/linux/sys_seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go index a8983705b..4ed52c4a7 100644 --- a/pkg/sentry/syscalls/linux/sys_sem.go +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_shm.go b/pkg/sentry/syscalls/linux/sys_shm.go index 48ff1d5f0..b13d48b98 100644 --- a/pkg/sentry/syscalls/linux/sys_shm.go +++ b/pkg/sentry/syscalls/linux/sys_shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_signal.go b/pkg/sentry/syscalls/linux/sys_signal.go index ecdec5d3a..a539354c5 100644 --- a/pkg/sentry/syscalls/linux/sys_signal.go +++ b/pkg/sentry/syscalls/linux/sys_signal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_socket.go b/pkg/sentry/syscalls/linux/sys_socket.go index 5fa5ddce6..0a7551742 100644 --- a/pkg/sentry/syscalls/linux/sys_socket.go +++ b/pkg/sentry/syscalls/linux/sys_socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_stat.go b/pkg/sentry/syscalls/linux/sys_stat.go index 619a14d7c..9c433c45d 100644 --- a/pkg/sentry/syscalls/linux/sys_stat.go +++ b/pkg/sentry/syscalls/linux/sys_stat.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_sync.go b/pkg/sentry/syscalls/linux/sys_sync.go index 902d210db..826c6869d 100644 --- a/pkg/sentry/syscalls/linux/sys_sync.go +++ b/pkg/sentry/syscalls/linux/sys_sync.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_sysinfo.go b/pkg/sentry/syscalls/linux/sys_sysinfo.go index 6560bac57..5eeb3ba58 100644 --- a/pkg/sentry/syscalls/linux/sys_sysinfo.go +++ b/pkg/sentry/syscalls/linux/sys_sysinfo.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_syslog.go b/pkg/sentry/syscalls/linux/sys_syslog.go index 792040c81..7193b7aed 100644 --- a/pkg/sentry/syscalls/linux/sys_syslog.go +++ b/pkg/sentry/syscalls/linux/sys_syslog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go index 550f63a43..820ca680e 100644 --- a/pkg/sentry/syscalls/linux/sys_thread.go +++ b/pkg/sentry/syscalls/linux/sys_thread.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_time.go b/pkg/sentry/syscalls/linux/sys_time.go index 8e6683444..063fbb106 100644 --- a/pkg/sentry/syscalls/linux/sys_time.go +++ b/pkg/sentry/syscalls/linux/sys_time.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_timer.go b/pkg/sentry/syscalls/linux/sys_timer.go index c41074d54..6baf4599b 100644 --- a/pkg/sentry/syscalls/linux/sys_timer.go +++ b/pkg/sentry/syscalls/linux/sys_timer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_timerfd.go b/pkg/sentry/syscalls/linux/sys_timerfd.go index 92c6a3d60..f70d13682 100644 --- a/pkg/sentry/syscalls/linux/sys_timerfd.go +++ b/pkg/sentry/syscalls/linux/sys_timerfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_tls.go b/pkg/sentry/syscalls/linux/sys_tls.go index b95d62320..27ddb3808 100644 --- a/pkg/sentry/syscalls/linux/sys_tls.go +++ b/pkg/sentry/syscalls/linux/sys_tls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_utsname.go b/pkg/sentry/syscalls/linux/sys_utsname.go index 899116374..689f2f838 100644 --- a/pkg/sentry/syscalls/linux/sys_utsname.go +++ b/pkg/sentry/syscalls/linux/sys_utsname.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/sys_write.go b/pkg/sentry/syscalls/linux/sys_write.go index caa7b01ea..08e263112 100644 --- a/pkg/sentry/syscalls/linux/sys_write.go +++ b/pkg/sentry/syscalls/linux/sys_write.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/linux/timespec.go b/pkg/sentry/syscalls/linux/timespec.go index e865c6fc0..752ec326d 100644 --- a/pkg/sentry/syscalls/linux/timespec.go +++ b/pkg/sentry/syscalls/linux/timespec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/polling.go b/pkg/sentry/syscalls/polling.go index fd90184ef..2b33d6c19 100644 --- a/pkg/sentry/syscalls/polling.go +++ b/pkg/sentry/syscalls/polling.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go index 1176f858d..bae32d727 100644 --- a/pkg/sentry/syscalls/syscalls.go +++ b/pkg/sentry/syscalls/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/syscalls/unimplemented_syscall.proto b/pkg/sentry/syscalls/unimplemented_syscall.proto index d6febf5b1..41579b016 100644 --- a/pkg/sentry/syscalls/unimplemented_syscall.proto +++ b/pkg/sentry/syscalls/unimplemented_syscall.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/calibrated_clock.go b/pkg/sentry/time/calibrated_clock.go index cbb95e2d7..c8cf4eca4 100644 --- a/pkg/sentry/time/calibrated_clock.go +++ b/pkg/sentry/time/calibrated_clock.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/calibrated_clock_test.go b/pkg/sentry/time/calibrated_clock_test.go index 8b6dd5592..a9237630e 100644 --- a/pkg/sentry/time/calibrated_clock_test.go +++ b/pkg/sentry/time/calibrated_clock_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/clock_id.go b/pkg/sentry/time/clock_id.go index 500102e58..1317a5dad 100644 --- a/pkg/sentry/time/clock_id.go +++ b/pkg/sentry/time/clock_id.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/clocks.go b/pkg/sentry/time/clocks.go index 9925b407d..e26386520 100644 --- a/pkg/sentry/time/clocks.go +++ b/pkg/sentry/time/clocks.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/muldiv_amd64.s b/pkg/sentry/time/muldiv_amd64.s index 291940b1d..bfcb8c724 100644 --- a/pkg/sentry/time/muldiv_amd64.s +++ b/pkg/sentry/time/muldiv_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/parameters.go b/pkg/sentry/time/parameters.go index 594b4874b..f3ad58454 100644 --- a/pkg/sentry/time/parameters.go +++ b/pkg/sentry/time/parameters.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/parameters_test.go b/pkg/sentry/time/parameters_test.go index 7394fc5ee..4a0c4e880 100644 --- a/pkg/sentry/time/parameters_test.go +++ b/pkg/sentry/time/parameters_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/sampler.go b/pkg/sentry/time/sampler.go index cf581b5fa..445690d49 100644 --- a/pkg/sentry/time/sampler.go +++ b/pkg/sentry/time/sampler.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/sampler_test.go b/pkg/sentry/time/sampler_test.go index caf7e5c53..ec0e442b6 100644 --- a/pkg/sentry/time/sampler_test.go +++ b/pkg/sentry/time/sampler_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/sampler_unsafe.go b/pkg/sentry/time/sampler_unsafe.go index 7ea19d387..0f8eb4fc8 100644 --- a/pkg/sentry/time/sampler_unsafe.go +++ b/pkg/sentry/time/sampler_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/time/tsc_amd64.s b/pkg/sentry/time/tsc_amd64.s index 4cc604392..e53d477f7 100644 --- a/pkg/sentry/time/tsc_amd64.s +++ b/pkg/sentry/time/tsc_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/uniqueid/context.go b/pkg/sentry/uniqueid/context.go index e48fabc2d..399d98c29 100644 --- a/pkg/sentry/uniqueid/context.go +++ b/pkg/sentry/uniqueid/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usage/cpu.go b/pkg/sentry/usage/cpu.go index ed7b04b9e..cbd7cfe19 100644 --- a/pkg/sentry/usage/cpu.go +++ b/pkg/sentry/usage/cpu.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usage/io.go b/pkg/sentry/usage/io.go index 49faa507d..8e27a0a88 100644 --- a/pkg/sentry/usage/io.go +++ b/pkg/sentry/usage/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usage/memory.go b/pkg/sentry/usage/memory.go index 92a478d85..7e065cb76 100644 --- a/pkg/sentry/usage/memory.go +++ b/pkg/sentry/usage/memory.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usage/memory_unsafe.go b/pkg/sentry/usage/memory_unsafe.go index f990a7750..a3ae668a5 100644 --- a/pkg/sentry/usage/memory_unsafe.go +++ b/pkg/sentry/usage/memory_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usage/usage.go b/pkg/sentry/usage/usage.go index 3b3118659..ab327f8e2 100644 --- a/pkg/sentry/usage/usage.go +++ b/pkg/sentry/usage/usage.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/access_type.go b/pkg/sentry/usermem/access_type.go index 75346d854..c71d05afe 100644 --- a/pkg/sentry/usermem/access_type.go +++ b/pkg/sentry/usermem/access_type.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/addr.go b/pkg/sentry/usermem/addr.go index fc94bee80..2a75aa60c 100644 --- a/pkg/sentry/usermem/addr.go +++ b/pkg/sentry/usermem/addr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/addr_range_seq_test.go b/pkg/sentry/usermem/addr_range_seq_test.go index cf9d785ed..bd6a1ec8a 100644 --- a/pkg/sentry/usermem/addr_range_seq_test.go +++ b/pkg/sentry/usermem/addr_range_seq_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/addr_range_seq_unsafe.go b/pkg/sentry/usermem/addr_range_seq_unsafe.go index 13b2998b3..f5fd446fa 100644 --- a/pkg/sentry/usermem/addr_range_seq_unsafe.go +++ b/pkg/sentry/usermem/addr_range_seq_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/bytes_io.go b/pkg/sentry/usermem/bytes_io.go index 01a746404..274f568d0 100644 --- a/pkg/sentry/usermem/bytes_io.go +++ b/pkg/sentry/usermem/bytes_io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/bytes_io_unsafe.go b/pkg/sentry/usermem/bytes_io_unsafe.go index efd71fcbc..8bdf3a508 100644 --- a/pkg/sentry/usermem/bytes_io_unsafe.go +++ b/pkg/sentry/usermem/bytes_io_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/usermem.go b/pkg/sentry/usermem/usermem.go index 5d8a1c558..1d6c0b4d6 100644 --- a/pkg/sentry/usermem/usermem.go +++ b/pkg/sentry/usermem/usermem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/usermem_test.go b/pkg/sentry/usermem/usermem_test.go index 563560da8..1991a9641 100644 --- a/pkg/sentry/usermem/usermem_test.go +++ b/pkg/sentry/usermem/usermem_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/usermem/usermem_x86.go b/pkg/sentry/usermem/usermem_x86.go index 2484b0d82..9ec90f9ff 100644 --- a/pkg/sentry/usermem/usermem_x86.go +++ b/pkg/sentry/usermem/usermem_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sentry/watchdog/watchdog.go b/pkg/sentry/watchdog/watchdog.go index 5b620693d..75b11237f 100644 --- a/pkg/sentry/watchdog/watchdog.go +++ b/pkg/sentry/watchdog/watchdog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sleep/commit_amd64.s b/pkg/sleep/commit_amd64.s index d525e5b79..d08df7f37 100644 --- a/pkg/sleep/commit_amd64.s +++ b/pkg/sleep/commit_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sleep/commit_asm.go b/pkg/sleep/commit_asm.go index 39a55df7e..90eef4cbc 100644 --- a/pkg/sleep/commit_asm.go +++ b/pkg/sleep/commit_asm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sleep/commit_noasm.go b/pkg/sleep/commit_noasm.go index 584866cd8..967d22e24 100644 --- a/pkg/sleep/commit_noasm.go +++ b/pkg/sleep/commit_noasm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sleep/empty.s b/pkg/sleep/empty.s index 8aca31bee..85d52cd9c 100644 --- a/pkg/sleep/empty.s +++ b/pkg/sleep/empty.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sleep/sleep_test.go b/pkg/sleep/sleep_test.go index bc1738371..8feb9ffc2 100644 --- a/pkg/sleep/sleep_test.go +++ b/pkg/sleep/sleep_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sleep/sleep_unsafe.go b/pkg/sleep/sleep_unsafe.go index b12cce681..45fb6f0ea 100644 --- a/pkg/sleep/sleep_unsafe.go +++ b/pkg/sleep/sleep_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/decode.go b/pkg/state/decode.go index 3ef59610b..54b5ad8b8 100644 --- a/pkg/state/decode.go +++ b/pkg/state/decode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/encode.go b/pkg/state/encode.go index fd052db12..577aaf051 100644 --- a/pkg/state/encode.go +++ b/pkg/state/encode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/encode_unsafe.go b/pkg/state/encode_unsafe.go index d96ba56d4..be94742a8 100644 --- a/pkg/state/encode_unsafe.go +++ b/pkg/state/encode_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/map.go b/pkg/state/map.go index c3d165501..0035d7250 100644 --- a/pkg/state/map.go +++ b/pkg/state/map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/object.proto b/pkg/state/object.proto index c78efed2a..d3b46ea97 100644 --- a/pkg/state/object.proto +++ b/pkg/state/object.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/printer.go b/pkg/state/printer.go index 2c8ce60a5..aee4b69fb 100644 --- a/pkg/state/printer.go +++ b/pkg/state/printer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/state.go b/pkg/state/state.go index 23a0b5922..4b141777e 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index 38ad9da9c..22bcad9e1 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/statefile/statefile.go b/pkg/state/statefile/statefile.go index 9c86c1934..99158fd02 100644 --- a/pkg/state/statefile/statefile.go +++ b/pkg/state/statefile/statefile.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/statefile/statefile_test.go b/pkg/state/statefile/statefile_test.go index fa3fb9f2c..b4f400e01 100644 --- a/pkg/state/statefile/statefile_test.go +++ b/pkg/state/statefile/statefile_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/state/stats.go b/pkg/state/stats.go index ddcc49f78..17ca258fc 100644 --- a/pkg/state/stats.go +++ b/pkg/state/stats.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/atomicptr_unsafe.go b/pkg/sync/atomicptr_unsafe.go index f12e9cb67..d943b7ff4 100644 --- a/pkg/sync/atomicptr_unsafe.go +++ b/pkg/sync/atomicptr_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/atomicptrtest/atomicptr_test.go b/pkg/sync/atomicptrtest/atomicptr_test.go index b458382b1..3262785ce 100644 --- a/pkg/sync/atomicptrtest/atomicptr_test.go +++ b/pkg/sync/atomicptrtest/atomicptr_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/memmove_unsafe.go b/pkg/sync/memmove_unsafe.go index 0c992d5a4..cd7a02dca 100644 --- a/pkg/sync/memmove_unsafe.go +++ b/pkg/sync/memmove_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/norace_unsafe.go b/pkg/sync/norace_unsafe.go index 968665078..1593b9e5d 100644 --- a/pkg/sync/norace_unsafe.go +++ b/pkg/sync/norace_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/race_unsafe.go b/pkg/sync/race_unsafe.go index d143a21c7..473eaddc6 100644 --- a/pkg/sync/race_unsafe.go +++ b/pkg/sync/race_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/seqatomic_unsafe.go b/pkg/sync/seqatomic_unsafe.go index a18e1229a..bea31adc5 100644 --- a/pkg/sync/seqatomic_unsafe.go +++ b/pkg/sync/seqatomic_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/seqatomictest/seqatomic_test.go b/pkg/sync/seqatomictest/seqatomic_test.go index b785d2344..f5e1fbfff 100644 --- a/pkg/sync/seqatomictest/seqatomic_test.go +++ b/pkg/sync/seqatomictest/seqatomic_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/seqcount.go b/pkg/sync/seqcount.go index 8e3304d69..732e856a4 100644 --- a/pkg/sync/seqcount.go +++ b/pkg/sync/seqcount.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/seqcount_test.go b/pkg/sync/seqcount_test.go index fa4abed1d..b14a8878e 100644 --- a/pkg/sync/seqcount_test.go +++ b/pkg/sync/seqcount_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/sync/sync.go b/pkg/sync/sync.go index 36d4c4dee..22c5348d7 100644 --- a/pkg/sync/sync.go +++ b/pkg/sync/sync.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/syserr/host_linux.go b/pkg/syserr/host_linux.go index 22009a799..74bbe9f5b 100644 --- a/pkg/syserr/host_linux.go +++ b/pkg/syserr/host_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/syserr/netstack.go b/pkg/syserr/netstack.go index b9786b48f..20e756edb 100644 --- a/pkg/syserr/netstack.go +++ b/pkg/syserr/netstack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/syserr/syserr.go b/pkg/syserr/syserr.go index dba6cb7de..6a66e23a2 100644 --- a/pkg/syserr/syserr.go +++ b/pkg/syserr/syserr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/syserror/syserror.go b/pkg/syserror/syserror.go index 5bc74e65e..4228707f4 100644 --- a/pkg/syserror/syserror.go +++ b/pkg/syserror/syserror.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/syserror/syserror_test.go b/pkg/syserror/syserror_test.go index fb7d8d5ee..0f0da5781 100644 --- a/pkg/syserror/syserror_test.go +++ b/pkg/syserror/syserror_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/adapters/gonet/gonet.go b/pkg/tcpip/adapters/gonet/gonet.go index b64dce720..81428770b 100644 --- a/pkg/tcpip/adapters/gonet/gonet.go +++ b/pkg/tcpip/adapters/gonet/gonet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/adapters/gonet/gonet_test.go b/pkg/tcpip/adapters/gonet/gonet_test.go index 79b7c77ee..05a730a05 100644 --- a/pkg/tcpip/adapters/gonet/gonet_test.go +++ b/pkg/tcpip/adapters/gonet/gonet_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/buffer/prependable.go b/pkg/tcpip/buffer/prependable.go index c5dd2819f..d3a9a0f88 100644 --- a/pkg/tcpip/buffer/prependable.go +++ b/pkg/tcpip/buffer/prependable.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/buffer/view.go b/pkg/tcpip/buffer/view.go index cea4e3657..24479ea40 100644 --- a/pkg/tcpip/buffer/view.go +++ b/pkg/tcpip/buffer/view.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/buffer/view_test.go b/pkg/tcpip/buffer/view_test.go index 02c264593..74a0a96fc 100644 --- a/pkg/tcpip/buffer/view_test.go +++ b/pkg/tcpip/buffer/view_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/checker/checker.go b/pkg/tcpip/checker/checker.go index 206531f20..5dfb3ca1d 100644 --- a/pkg/tcpip/checker/checker.go +++ b/pkg/tcpip/checker/checker.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/arp.go b/pkg/tcpip/header/arp.go index ae373f112..22b259ccb 100644 --- a/pkg/tcpip/header/arp.go +++ b/pkg/tcpip/header/arp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/checksum.go b/pkg/tcpip/header/checksum.go index e67c50f50..12f208fde 100644 --- a/pkg/tcpip/header/checksum.go +++ b/pkg/tcpip/header/checksum.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/eth.go b/pkg/tcpip/header/eth.go index 99c29b750..77365bc41 100644 --- a/pkg/tcpip/header/eth.go +++ b/pkg/tcpip/header/eth.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/gue.go b/pkg/tcpip/header/gue.go index aac4593c5..2ad13955a 100644 --- a/pkg/tcpip/header/gue.go +++ b/pkg/tcpip/header/gue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/icmpv4.go b/pkg/tcpip/header/icmpv4.go index af1e94b7f..3ac89cdae 100644 --- a/pkg/tcpip/header/icmpv4.go +++ b/pkg/tcpip/header/icmpv4.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/icmpv6.go b/pkg/tcpip/header/icmpv6.go index 7d35caff7..e317975e8 100644 --- a/pkg/tcpip/header/icmpv6.go +++ b/pkg/tcpip/header/icmpv6.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/interfaces.go b/pkg/tcpip/header/interfaces.go index 042006983..ac327d8a5 100644 --- a/pkg/tcpip/header/interfaces.go +++ b/pkg/tcpip/header/interfaces.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/ipv4.go b/pkg/tcpip/header/ipv4.go index 29570cc34..1b882d3d8 100644 --- a/pkg/tcpip/header/ipv4.go +++ b/pkg/tcpip/header/ipv4.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/ipv6.go b/pkg/tcpip/header/ipv6.go index 66c778fe1..d985b745d 100644 --- a/pkg/tcpip/header/ipv6.go +++ b/pkg/tcpip/header/ipv6.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/ipv6_fragment.go b/pkg/tcpip/header/ipv6_fragment.go index 44b28b326..e36d5177b 100644 --- a/pkg/tcpip/header/ipv6_fragment.go +++ b/pkg/tcpip/header/ipv6_fragment.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/ipversion_test.go b/pkg/tcpip/header/ipversion_test.go index 3ae9b7e4a..8301ba5cf 100644 --- a/pkg/tcpip/header/ipversion_test.go +++ b/pkg/tcpip/header/ipversion_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/tcp.go b/pkg/tcpip/header/tcp.go index 6689a6dc5..567a21167 100644 --- a/pkg/tcpip/header/tcp.go +++ b/pkg/tcpip/header/tcp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/tcp_test.go b/pkg/tcpip/header/tcp_test.go index 7854d3523..7cd98df3b 100644 --- a/pkg/tcpip/header/tcp_test.go +++ b/pkg/tcpip/header/tcp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/header/udp.go b/pkg/tcpip/header/udp.go index cf2602e50..31c8ef456 100644 --- a/pkg/tcpip/header/udp.go +++ b/pkg/tcpip/header/udp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go index 113cbbf5e..da34032cc 100644 --- a/pkg/tcpip/link/channel/channel.go +++ b/pkg/tcpip/link/channel/channel.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/fdbased/endpoint.go b/pkg/tcpip/link/fdbased/endpoint.go index ee99ada07..24af428dd 100644 --- a/pkg/tcpip/link/fdbased/endpoint.go +++ b/pkg/tcpip/link/fdbased/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/fdbased/endpoint_test.go b/pkg/tcpip/link/fdbased/endpoint_test.go index 52e532ebb..19b007a9e 100644 --- a/pkg/tcpip/link/fdbased/endpoint_test.go +++ b/pkg/tcpip/link/fdbased/endpoint_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/loopback/loopback.go b/pkg/tcpip/link/loopback/loopback.go index fc3f80c01..e6585be66 100644 --- a/pkg/tcpip/link/loopback/loopback.go +++ b/pkg/tcpip/link/loopback/loopback.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/rawfile/blockingpoll_amd64.s b/pkg/tcpip/link/rawfile/blockingpoll_amd64.s index fc5231831..63b8c4451 100644 --- a/pkg/tcpip/link/rawfile/blockingpoll_amd64.s +++ b/pkg/tcpip/link/rawfile/blockingpoll_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go b/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go index a0a9d4acd..6a3e956ad 100644 --- a/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go +++ b/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/rawfile/blockingpoll_unsafe_amd64.go b/pkg/tcpip/link/rawfile/blockingpoll_unsafe_amd64.go index 1f143c0db..89a8a9954 100644 --- a/pkg/tcpip/link/rawfile/blockingpoll_unsafe_amd64.go +++ b/pkg/tcpip/link/rawfile/blockingpoll_unsafe_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/rawfile/errors.go b/pkg/tcpip/link/rawfile/errors.go index de7593d9c..f42ff98db 100644 --- a/pkg/tcpip/link/rawfile/errors.go +++ b/pkg/tcpip/link/rawfile/errors.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/rawfile/rawfile_unsafe.go b/pkg/tcpip/link/rawfile/rawfile_unsafe.go index cea3cd6a1..be4a4fa9c 100644 --- a/pkg/tcpip/link/rawfile/rawfile_unsafe.go +++ b/pkg/tcpip/link/rawfile/rawfile_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/pipe/pipe.go b/pkg/tcpip/link/sharedmem/pipe/pipe.go index 1a0edbaba..e014324cc 100644 --- a/pkg/tcpip/link/sharedmem/pipe/pipe.go +++ b/pkg/tcpip/link/sharedmem/pipe/pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/pipe/pipe_test.go b/pkg/tcpip/link/sharedmem/pipe/pipe_test.go index db0737c98..30742ccb1 100644 --- a/pkg/tcpip/link/sharedmem/pipe/pipe_test.go +++ b/pkg/tcpip/link/sharedmem/pipe/pipe_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go b/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go index 480dc4a23..f491d74a2 100644 --- a/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go +++ b/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/pipe/rx.go b/pkg/tcpip/link/sharedmem/pipe/rx.go index ff778cecd..8d641c76f 100644 --- a/pkg/tcpip/link/sharedmem/pipe/rx.go +++ b/pkg/tcpip/link/sharedmem/pipe/rx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/pipe/tx.go b/pkg/tcpip/link/sharedmem/pipe/tx.go index 717f5a4b1..e75175d98 100644 --- a/pkg/tcpip/link/sharedmem/pipe/tx.go +++ b/pkg/tcpip/link/sharedmem/pipe/tx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/queue/queue_test.go b/pkg/tcpip/link/sharedmem/queue/queue_test.go index 3d5909cef..391165bc3 100644 --- a/pkg/tcpip/link/sharedmem/queue/queue_test.go +++ b/pkg/tcpip/link/sharedmem/queue/queue_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/queue/rx.go b/pkg/tcpip/link/sharedmem/queue/rx.go index c40d62c33..d3a5da08a 100644 --- a/pkg/tcpip/link/sharedmem/queue/rx.go +++ b/pkg/tcpip/link/sharedmem/queue/rx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/queue/tx.go b/pkg/tcpip/link/sharedmem/queue/tx.go index 39b595e56..845108db1 100644 --- a/pkg/tcpip/link/sharedmem/queue/tx.go +++ b/pkg/tcpip/link/sharedmem/queue/tx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/rx.go b/pkg/tcpip/link/sharedmem/rx.go index b8e39eca1..3eeab769e 100644 --- a/pkg/tcpip/link/sharedmem/rx.go +++ b/pkg/tcpip/link/sharedmem/rx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/sharedmem.go b/pkg/tcpip/link/sharedmem/sharedmem.go index ce6e86767..27d7eb3b9 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem.go +++ b/pkg/tcpip/link/sharedmem/sharedmem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/sharedmem_test.go b/pkg/tcpip/link/sharedmem/sharedmem_test.go index ad987d382..4b8061b13 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_test.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go b/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go index f0be2dc73..b91adbaf7 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sharedmem/tx.go b/pkg/tcpip/link/sharedmem/tx.go index 42a21cb43..37da34831 100644 --- a/pkg/tcpip/link/sharedmem/tx.go +++ b/pkg/tcpip/link/sharedmem/tx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sniffer/pcap.go b/pkg/tcpip/link/sniffer/pcap.go index 04f3d494e..3d0d8d852 100644 --- a/pkg/tcpip/link/sniffer/pcap.go +++ b/pkg/tcpip/link/sniffer/pcap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/sniffer/sniffer.go b/pkg/tcpip/link/sniffer/sniffer.go index a30e57a32..1bd174bc3 100644 --- a/pkg/tcpip/link/sniffer/sniffer.go +++ b/pkg/tcpip/link/sniffer/sniffer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/tun/tun_unsafe.go b/pkg/tcpip/link/tun/tun_unsafe.go index 1dec41982..e4c589dda 100644 --- a/pkg/tcpip/link/tun/tun_unsafe.go +++ b/pkg/tcpip/link/tun/tun_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/waitable/waitable.go b/pkg/tcpip/link/waitable/waitable.go index ef8c88561..9ffb7b7e9 100644 --- a/pkg/tcpip/link/waitable/waitable.go +++ b/pkg/tcpip/link/waitable/waitable.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/link/waitable/waitable_test.go b/pkg/tcpip/link/waitable/waitable_test.go index 0a15c40de..5ebe09664 100644 --- a/pkg/tcpip/link/waitable/waitable_test.go +++ b/pkg/tcpip/link/waitable/waitable_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/arp/arp.go b/pkg/tcpip/network/arp/arp.go index 9d0881e11..2e0024925 100644 --- a/pkg/tcpip/network/arp/arp.go +++ b/pkg/tcpip/network/arp/arp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/arp/arp_test.go b/pkg/tcpip/network/arp/arp_test.go index 50628e4a2..5894f9114 100644 --- a/pkg/tcpip/network/arp/arp_test.go +++ b/pkg/tcpip/network/arp/arp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/fragmentation/frag_heap.go b/pkg/tcpip/network/fragmentation/frag_heap.go index 6c7faafe4..55615c8e6 100644 --- a/pkg/tcpip/network/fragmentation/frag_heap.go +++ b/pkg/tcpip/network/fragmentation/frag_heap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/fragmentation/frag_heap_test.go b/pkg/tcpip/network/fragmentation/frag_heap_test.go index a15540634..1b1b72e88 100644 --- a/pkg/tcpip/network/fragmentation/frag_heap_test.go +++ b/pkg/tcpip/network/fragmentation/frag_heap_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/fragmentation/fragmentation.go b/pkg/tcpip/network/fragmentation/fragmentation.go index 885e3cca2..a5dda0398 100644 --- a/pkg/tcpip/network/fragmentation/fragmentation.go +++ b/pkg/tcpip/network/fragmentation/fragmentation.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/fragmentation/fragmentation_test.go b/pkg/tcpip/network/fragmentation/fragmentation_test.go index fc62a15dd..5bf3463a9 100644 --- a/pkg/tcpip/network/fragmentation/fragmentation_test.go +++ b/pkg/tcpip/network/fragmentation/fragmentation_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/fragmentation/reassembler.go b/pkg/tcpip/network/fragmentation/reassembler.go index b57fe82ec..c9ad2bef6 100644 --- a/pkg/tcpip/network/fragmentation/reassembler.go +++ b/pkg/tcpip/network/fragmentation/reassembler.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/fragmentation/reassembler_test.go b/pkg/tcpip/network/fragmentation/reassembler_test.go index 4c137828f..a2bc9707a 100644 --- a/pkg/tcpip/network/fragmentation/reassembler_test.go +++ b/pkg/tcpip/network/fragmentation/reassembler_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/hash/hash.go b/pkg/tcpip/network/hash/hash.go index eddf7ca4d..07960ddf0 100644 --- a/pkg/tcpip/network/hash/hash.go +++ b/pkg/tcpip/network/hash/hash.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/ip_test.go b/pkg/tcpip/network/ip_test.go index e3c7af1f9..5c1e88e56 100644 --- a/pkg/tcpip/network/ip_test.go +++ b/pkg/tcpip/network/ip_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/ipv4/icmp.go b/pkg/tcpip/network/ipv4/icmp.go index ee8172ac8..f82dc098f 100644 --- a/pkg/tcpip/network/ipv4/icmp.go +++ b/pkg/tcpip/network/ipv4/icmp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index d4eeeb5d9..d7801ec19 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/ipv4/ipv4_test.go b/pkg/tcpip/network/ipv4/ipv4_test.go index 2b7067a50..190d548eb 100644 --- a/pkg/tcpip/network/ipv4/ipv4_test.go +++ b/pkg/tcpip/network/ipv4/ipv4_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go index 81aba0923..14107443b 100644 --- a/pkg/tcpip/network/ipv6/icmp.go +++ b/pkg/tcpip/network/ipv6/icmp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/ipv6/icmp_test.go b/pkg/tcpip/network/ipv6/icmp_test.go index fabbdc8c7..12c818b48 100644 --- a/pkg/tcpip/network/ipv6/icmp_test.go +++ b/pkg/tcpip/network/ipv6/icmp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go index 25bd998e5..4d0b6ee9c 100644 --- a/pkg/tcpip/network/ipv6/ipv6.go +++ b/pkg/tcpip/network/ipv6/ipv6.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/ports/ports.go b/pkg/tcpip/ports/ports.go index 4e24efddb..41ef32921 100644 --- a/pkg/tcpip/ports/ports.go +++ b/pkg/tcpip/ports/ports.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/ports/ports_test.go b/pkg/tcpip/ports/ports_test.go index 4ab6a1fa2..72577dfcb 100644 --- a/pkg/tcpip/ports/ports_test.go +++ b/pkg/tcpip/ports/ports_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/sample/tun_tcp_connect/main.go b/pkg/tcpip/sample/tun_tcp_connect/main.go index c4707736e..67e8f0b9e 100644 --- a/pkg/tcpip/sample/tun_tcp_connect/main.go +++ b/pkg/tcpip/sample/tun_tcp_connect/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/sample/tun_tcp_echo/main.go b/pkg/tcpip/sample/tun_tcp_echo/main.go index 910d1257f..ab40e9e0b 100644 --- a/pkg/tcpip/sample/tun_tcp_echo/main.go +++ b/pkg/tcpip/sample/tun_tcp_echo/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/seqnum/seqnum.go b/pkg/tcpip/seqnum/seqnum.go index e507d02f7..f2b988839 100644 --- a/pkg/tcpip/seqnum/seqnum.go +++ b/pkg/tcpip/seqnum/seqnum.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/linkaddrcache.go b/pkg/tcpip/stack/linkaddrcache.go index 3a147a75f..cb7b7116b 100644 --- a/pkg/tcpip/stack/linkaddrcache.go +++ b/pkg/tcpip/stack/linkaddrcache.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/linkaddrcache_test.go b/pkg/tcpip/stack/linkaddrcache_test.go index e46267f12..651fa17ac 100644 --- a/pkg/tcpip/stack/linkaddrcache_test.go +++ b/pkg/tcpip/stack/linkaddrcache_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index dba95369c..3da99ac67 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index 0acec2984..b6266eb55 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/route.go b/pkg/tcpip/stack/route.go index 6c6400c33..2b4185014 100644 --- a/pkg/tcpip/stack/route.go +++ b/pkg/tcpip/stack/route.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index d1ec6a660..d4da980a9 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/stack_global_state.go b/pkg/tcpip/stack/stack_global_state.go index b6c095efb..f2c6c9a8d 100644 --- a/pkg/tcpip/stack/stack_global_state.go +++ b/pkg/tcpip/stack/stack_global_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/stack_test.go b/pkg/tcpip/stack/stack_test.go index a0b3399a8..74bf2c99e 100644 --- a/pkg/tcpip/stack/stack_test.go +++ b/pkg/tcpip/stack/stack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/transport_demuxer.go b/pkg/tcpip/stack/transport_demuxer.go index a7470d606..c8522ad9e 100644 --- a/pkg/tcpip/stack/transport_demuxer.go +++ b/pkg/tcpip/stack/transport_demuxer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/stack/transport_test.go b/pkg/tcpip/stack/transport_test.go index 98cc3b120..f09760180 100644 --- a/pkg/tcpip/stack/transport_test.go +++ b/pkg/tcpip/stack/transport_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index bf11c2175..413aee6c6 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/tcpip_test.go b/pkg/tcpip/tcpip_test.go index d283f71c7..361e359d4 100644 --- a/pkg/tcpip/tcpip_test.go +++ b/pkg/tcpip/tcpip_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/time.s b/pkg/tcpip/time.s index 8aca31bee..85d52cd9c 100644 --- a/pkg/tcpip/time.s +++ b/pkg/tcpip/time.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/time_unsafe.go b/pkg/tcpip/time_unsafe.go index 2102e9633..231151bf3 100644 --- a/pkg/tcpip/time_unsafe.go +++ b/pkg/tcpip/time_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/ping/endpoint.go b/pkg/tcpip/transport/ping/endpoint.go index 055daa918..b3f54cfe0 100644 --- a/pkg/tcpip/transport/ping/endpoint.go +++ b/pkg/tcpip/transport/ping/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/ping/endpoint_state.go b/pkg/tcpip/transport/ping/endpoint_state.go index a16087304..80721d227 100644 --- a/pkg/tcpip/transport/ping/endpoint_state.go +++ b/pkg/tcpip/transport/ping/endpoint_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/ping/protocol.go b/pkg/tcpip/transport/ping/protocol.go index 549b1b2d3..1d504773b 100644 --- a/pkg/tcpip/transport/ping/protocol.go +++ b/pkg/tcpip/transport/ping/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/accept.go b/pkg/tcpip/transport/tcp/accept.go index c22ed5ea7..5a88d25d0 100644 --- a/pkg/tcpip/transport/tcp/accept.go +++ b/pkg/tcpip/transport/tcp/accept.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/connect.go b/pkg/tcpip/transport/tcp/connect.go index 27dbcace2..800d2409e 100644 --- a/pkg/tcpip/transport/tcp/connect.go +++ b/pkg/tcpip/transport/tcp/connect.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/cubic.go b/pkg/tcpip/transport/tcp/cubic.go index 8cea416d2..003525d86 100644 --- a/pkg/tcpip/transport/tcp/cubic.go +++ b/pkg/tcpip/transport/tcp/cubic.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/dual_stack_test.go b/pkg/tcpip/transport/tcp/dual_stack_test.go index c88e98977..d3120c1d8 100644 --- a/pkg/tcpip/transport/tcp/dual_stack_test.go +++ b/pkg/tcpip/transport/tcp/dual_stack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/endpoint.go b/pkg/tcpip/transport/tcp/endpoint.go index 707d6be96..673a65c31 100644 --- a/pkg/tcpip/transport/tcp/endpoint.go +++ b/pkg/tcpip/transport/tcp/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/endpoint_state.go b/pkg/tcpip/transport/tcp/endpoint_state.go index bed7ec6a6..e32c73aae 100644 --- a/pkg/tcpip/transport/tcp/endpoint_state.go +++ b/pkg/tcpip/transport/tcp/endpoint_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/forwarder.go b/pkg/tcpip/transport/tcp/forwarder.go index c80f3c7d6..2f90839e9 100644 --- a/pkg/tcpip/transport/tcp/forwarder.go +++ b/pkg/tcpip/transport/tcp/forwarder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/protocol.go b/pkg/tcpip/transport/tcp/protocol.go index abdc825cd..753e1419e 100644 --- a/pkg/tcpip/transport/tcp/protocol.go +++ b/pkg/tcpip/transport/tcp/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/rcv.go b/pkg/tcpip/transport/tcp/rcv.go index 92ef9c6f7..05ff9e0d7 100644 --- a/pkg/tcpip/transport/tcp/rcv.go +++ b/pkg/tcpip/transport/tcp/rcv.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/reno.go b/pkg/tcpip/transport/tcp/reno.go index feb593234..e4f8b7d5a 100644 --- a/pkg/tcpip/transport/tcp/reno.go +++ b/pkg/tcpip/transport/tcp/reno.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/sack.go b/pkg/tcpip/transport/tcp/sack.go index 05bac08cb..24e48fe7b 100644 --- a/pkg/tcpip/transport/tcp/sack.go +++ b/pkg/tcpip/transport/tcp/sack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/segment.go b/pkg/tcpip/transport/tcp/segment.go index 51a3d6aba..fc87a05fd 100644 --- a/pkg/tcpip/transport/tcp/segment.go +++ b/pkg/tcpip/transport/tcp/segment.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/segment_heap.go b/pkg/tcpip/transport/tcp/segment_heap.go index e3a3405ef..98422fadf 100644 --- a/pkg/tcpip/transport/tcp/segment_heap.go +++ b/pkg/tcpip/transport/tcp/segment_heap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/segment_queue.go b/pkg/tcpip/transport/tcp/segment_queue.go index 6a2d7bc0b..0c637d7ad 100644 --- a/pkg/tcpip/transport/tcp/segment_queue.go +++ b/pkg/tcpip/transport/tcp/segment_queue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/segment_state.go b/pkg/tcpip/transport/tcp/segment_state.go index 22f0bbf18..46b6d85a6 100644 --- a/pkg/tcpip/transport/tcp/segment_state.go +++ b/pkg/tcpip/transport/tcp/segment_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go index 0bd421ff4..eefe93d48 100644 --- a/pkg/tcpip/transport/tcp/snd.go +++ b/pkg/tcpip/transport/tcp/snd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/snd_state.go b/pkg/tcpip/transport/tcp/snd_state.go index d536839af..86bbd643f 100644 --- a/pkg/tcpip/transport/tcp/snd_state.go +++ b/pkg/tcpip/transport/tcp/snd_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/tcp_sack_test.go b/pkg/tcpip/transport/tcp/tcp_sack_test.go index a61d0ca64..06b0702c5 100644 --- a/pkg/tcpip/transport/tcp/tcp_sack_test.go +++ b/pkg/tcpip/transport/tcp/tcp_sack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/tcp_test.go b/pkg/tcpip/transport/tcp/tcp_test.go index 48852ea47..04e046257 100644 --- a/pkg/tcpip/transport/tcp/tcp_test.go +++ b/pkg/tcpip/transport/tcp/tcp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/tcp_timestamp_test.go b/pkg/tcpip/transport/tcp/tcp_timestamp_test.go index ca16fc8fa..b08df0fec 100644 --- a/pkg/tcpip/transport/tcp/tcp_timestamp_test.go +++ b/pkg/tcpip/transport/tcp/tcp_timestamp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/testing/context/context.go b/pkg/tcpip/transport/tcp/testing/context/context.go index 5b25534f4..0695e8150 100644 --- a/pkg/tcpip/transport/tcp/testing/context/context.go +++ b/pkg/tcpip/transport/tcp/testing/context/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcp/timer.go b/pkg/tcpip/transport/tcp/timer.go index 938c0bcef..38240d2d5 100644 --- a/pkg/tcpip/transport/tcp/timer.go +++ b/pkg/tcpip/transport/tcp/timer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go index 5f8f1a64d..f7b2900de 100644 --- a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go +++ b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go index 514722ab7..aaeae9b18 100644 --- a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go +++ b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/udp/endpoint.go b/pkg/tcpip/transport/udp/endpoint.go index 840e95302..d777a80d0 100644 --- a/pkg/tcpip/transport/udp/endpoint.go +++ b/pkg/tcpip/transport/udp/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/udp/endpoint_state.go b/pkg/tcpip/transport/udp/endpoint_state.go index 70a37c7f2..db1e281ad 100644 --- a/pkg/tcpip/transport/udp/endpoint_state.go +++ b/pkg/tcpip/transport/udp/endpoint_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/udp/protocol.go b/pkg/tcpip/transport/udp/protocol.go index 1334fec8a..b3fbed6e4 100644 --- a/pkg/tcpip/transport/udp/protocol.go +++ b/pkg/tcpip/transport/udp/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tcpip/transport/udp/udp_test.go b/pkg/tcpip/transport/udp/udp_test.go index c3f592bd4..58a346cd9 100644 --- a/pkg/tcpip/transport/udp/udp_test.go +++ b/pkg/tcpip/transport/udp/udp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tmutex/tmutex.go b/pkg/tmutex/tmutex.go index bd5c681dd..df61d89f5 100644 --- a/pkg/tmutex/tmutex.go +++ b/pkg/tmutex/tmutex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tmutex/tmutex_test.go b/pkg/tmutex/tmutex_test.go index a9dc9972f..a4537cb3b 100644 --- a/pkg/tmutex/tmutex_test.go +++ b/pkg/tmutex/tmutex_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/unet/unet.go b/pkg/unet/unet.go index f4800e0d9..deeea078d 100644 --- a/pkg/unet/unet.go +++ b/pkg/unet/unet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/unet/unet_test.go b/pkg/unet/unet_test.go index 6c546825f..ecc670925 100644 --- a/pkg/unet/unet_test.go +++ b/pkg/unet/unet_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/unet/unet_unsafe.go b/pkg/unet/unet_unsafe.go index fa15cf744..1d69de542 100644 --- a/pkg/unet/unet_unsafe.go +++ b/pkg/unet/unet_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/urpc/urpc.go b/pkg/urpc/urpc.go index 1ec06dd4c..753366be2 100644 --- a/pkg/urpc/urpc.go +++ b/pkg/urpc/urpc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/urpc/urpc_test.go b/pkg/urpc/urpc_test.go index d9cfc512e..f1b9a85ca 100644 --- a/pkg/urpc/urpc_test.go +++ b/pkg/urpc/urpc_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/waiter/fdnotifier/fdnotifier.go b/pkg/waiter/fdnotifier/fdnotifier.go index 8bb93e39b..624b1a0c5 100644 --- a/pkg/waiter/fdnotifier/fdnotifier.go +++ b/pkg/waiter/fdnotifier/fdnotifier.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/waiter/fdnotifier/poll_unsafe.go b/pkg/waiter/fdnotifier/poll_unsafe.go index 26bca2b53..8459d4c74 100644 --- a/pkg/waiter/fdnotifier/poll_unsafe.go +++ b/pkg/waiter/fdnotifier/poll_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/waiter/waiter.go b/pkg/waiter/waiter.go index 832b6a5a9..93390b299 100644 --- a/pkg/waiter/waiter.go +++ b/pkg/waiter/waiter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/waiter/waiter_test.go b/pkg/waiter/waiter_test.go index c45f22889..60853f9c1 100644 --- a/pkg/waiter/waiter_test.go +++ b/pkg/waiter/waiter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/compat.go b/runsc/boot/compat.go index 3250cdcdc..6766953b3 100644 --- a/runsc/boot/compat.go +++ b/runsc/boot/compat.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/config.go b/runsc/boot/config.go index 51d20d06d..9ebbde424 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index bee82f344..6dd7fadd9 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/debug.go b/runsc/boot/debug.go index 971962c91..d224d08b7 100644 --- a/runsc/boot/debug.go +++ b/runsc/boot/debug.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/events.go b/runsc/boot/events.go index 595846b10..f954b8c0b 100644 --- a/runsc/boot/events.go +++ b/runsc/boot/events.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/fds.go b/runsc/boot/fds.go index 9416e3a5c..a3d21d963 100644 --- a/runsc/boot/fds.go +++ b/runsc/boot/fds.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go index 92a73db9a..378396b9b 100644 --- a/runsc/boot/filter/config.go +++ b/runsc/boot/filter/config.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/filter/extra_filters.go b/runsc/boot/filter/extra_filters.go index 82cf00dfb..67f3101fe 100644 --- a/runsc/boot/filter/extra_filters.go +++ b/runsc/boot/filter/extra_filters.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/filter/extra_filters_msan.go b/runsc/boot/filter/extra_filters_msan.go index 76f3f6865..fb95283ab 100644 --- a/runsc/boot/filter/extra_filters_msan.go +++ b/runsc/boot/filter/extra_filters_msan.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/filter/extra_filters_race.go b/runsc/boot/filter/extra_filters_race.go index ebd56c553..02a122c95 100644 --- a/runsc/boot/filter/extra_filters_race.go +++ b/runsc/boot/filter/extra_filters_race.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/filter/filter.go b/runsc/boot/filter/filter.go index b656883ad..dc7294b1d 100644 --- a/runsc/boot/filter/filter.go +++ b/runsc/boot/filter/filter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index ea825e571..e52c89fe4 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/limits.go b/runsc/boot/limits.go index 510497eba..8ecda6d0e 100644 --- a/runsc/boot/limits.go +++ b/runsc/boot/limits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index c79b95bde..fa3de0133 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index 41ff3681b..c342ee005 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/network.go b/runsc/boot/network.go index 6a2678ac9..89f186139 100644 --- a/runsc/boot/network.go +++ b/runsc/boot/network.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/boot/strace.go b/runsc/boot/strace.go index 1e898672b..028bcc1f4 100644 --- a/runsc/boot/strace.go +++ b/runsc/boot/strace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go index 7a75a189a..d6058a8a2 100644 --- a/runsc/cgroup/cgroup.go +++ b/runsc/cgroup/cgroup.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cgroup/cgroup_test.go b/runsc/cgroup/cgroup_test.go index cde915329..4a4713d4f 100644 --- a/runsc/cgroup/cgroup_test.go +++ b/runsc/cgroup/cgroup_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 023b63dc0..7c14857ba 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/capability.go b/runsc/cmd/capability.go index 0b18c5481..e5da021e5 100644 --- a/runsc/cmd/capability.go +++ b/runsc/cmd/capability.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/capability_test.go b/runsc/cmd/capability_test.go index 3329b308d..dd278b32d 100644 --- a/runsc/cmd/capability_test.go +++ b/runsc/cmd/capability_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/checkpoint.go b/runsc/cmd/checkpoint.go index 023ab2455..d49d0169b 100644 --- a/runsc/cmd/checkpoint.go +++ b/runsc/cmd/checkpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/cmd.go b/runsc/cmd/cmd.go index 2937ae1c4..a1c3491a3 100644 --- a/runsc/cmd/cmd.go +++ b/runsc/cmd/cmd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 275a96f57..b84185b43 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/debug.go b/runsc/cmd/debug.go index cb7d81057..288cbe435 100644 --- a/runsc/cmd/debug.go +++ b/runsc/cmd/debug.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/delete.go b/runsc/cmd/delete.go index 92b609c3c..ea1ca1278 100644 --- a/runsc/cmd/delete.go +++ b/runsc/cmd/delete.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/delete_test.go b/runsc/cmd/delete_test.go index f6d164394..4a5b4774a 100644 --- a/runsc/cmd/delete_test.go +++ b/runsc/cmd/delete_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/events.go b/runsc/cmd/events.go index df65ea31d..df03415ec 100644 --- a/runsc/cmd/events.go +++ b/runsc/cmd/events.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index 336edf3f6..9a395e6f1 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/exec_test.go b/runsc/cmd/exec_test.go index 623461e78..686c5e150 100644 --- a/runsc/cmd/exec_test.go +++ b/runsc/cmd/exec_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index fd4eee546..3842fdf64 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/kill.go b/runsc/cmd/kill.go index 7a98d10a2..1f1086250 100644 --- a/runsc/cmd/kill.go +++ b/runsc/cmd/kill.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/list.go b/runsc/cmd/list.go index 4d4a5cb0b..fd59b73e6 100644 --- a/runsc/cmd/list.go +++ b/runsc/cmd/list.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/path.go b/runsc/cmd/path.go index c207b80da..baba937a8 100644 --- a/runsc/cmd/path.go +++ b/runsc/cmd/path.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/pause.go b/runsc/cmd/pause.go index ac393b48e..5ff6f059c 100644 --- a/runsc/cmd/pause.go +++ b/runsc/cmd/pause.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/ps.go b/runsc/cmd/ps.go index 5d219bfdc..fd76cf975 100644 --- a/runsc/cmd/ps.go +++ b/runsc/cmd/ps.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/restore.go b/runsc/cmd/restore.go index 6dc044672..cc99b3503 100644 --- a/runsc/cmd/restore.go +++ b/runsc/cmd/restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/resume.go b/runsc/cmd/resume.go index a12adf1a3..274b5d084 100644 --- a/runsc/cmd/resume.go +++ b/runsc/cmd/resume.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index 9a87cf240..b6a12f5d6 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/spec.go b/runsc/cmd/spec.go index 6281fc49d..57ee37c86 100644 --- a/runsc/cmd/spec.go +++ b/runsc/cmd/spec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/start.go b/runsc/cmd/start.go index 97ea91fff..48bd4c401 100644 --- a/runsc/cmd/start.go +++ b/runsc/cmd/start.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/state.go b/runsc/cmd/state.go index 265014e1b..f8ce8c3d8 100644 --- a/runsc/cmd/state.go +++ b/runsc/cmd/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/cmd/wait.go b/runsc/cmd/wait.go index 956349140..121c54554 100644 --- a/runsc/cmd/wait.go +++ b/runsc/cmd/wait.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/console/console.go b/runsc/console/console.go index 3df184742..9f4f9214d 100644 --- a/runsc/console/console.go +++ b/runsc/console/console.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/console_test.go b/runsc/container/console_test.go index 8f019b54a..0b0dfb4cb 100644 --- a/runsc/container/console_test.go +++ b/runsc/container/console_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/container.go b/runsc/container/container.go index f76bad1aa..cb4c9b5c1 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 662591b3b..243528d35 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/fs.go b/runsc/container/fs.go index 2ed42fd93..41022686b 100644 --- a/runsc/container/fs.go +++ b/runsc/container/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/fs_test.go b/runsc/container/fs_test.go index 84bde18fb..87cdb078e 100644 --- a/runsc/container/fs_test.go +++ b/runsc/container/fs_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/hook.go b/runsc/container/hook.go index 3d93ca0be..6b9e5550a 100644 --- a/runsc/container/hook.go +++ b/runsc/container/hook.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index 1781a4602..4548eb106 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/status.go b/runsc/container/status.go index bf177e78a..234ffb0dd 100644 --- a/runsc/container/status.go +++ b/runsc/container/status.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/container/test_app.go b/runsc/container/test_app.go index cc3b087e1..b5071ada6 100644 --- a/runsc/container/test_app.go +++ b/runsc/container/test_app.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/filter/config.go b/runsc/fsgofer/filter/config.go index 35698f21f..75a087848 100644 --- a/runsc/fsgofer/filter/config.go +++ b/runsc/fsgofer/filter/config.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/filter/extra_filters.go b/runsc/fsgofer/filter/extra_filters.go index 82cf00dfb..67f3101fe 100644 --- a/runsc/fsgofer/filter/extra_filters.go +++ b/runsc/fsgofer/filter/extra_filters.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/filter/extra_filters_msan.go b/runsc/fsgofer/filter/extra_filters_msan.go index 169a79ed8..7e142b790 100644 --- a/runsc/fsgofer/filter/extra_filters_msan.go +++ b/runsc/fsgofer/filter/extra_filters_msan.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/filter/extra_filters_race.go b/runsc/fsgofer/filter/extra_filters_race.go index 9e6512d8c..3cd29472a 100644 --- a/runsc/fsgofer/filter/extra_filters_race.go +++ b/runsc/fsgofer/filter/extra_filters_race.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/filter/filter.go b/runsc/fsgofer/filter/filter.go index 6f341f688..f50b6bc87 100644 --- a/runsc/fsgofer/filter/filter.go +++ b/runsc/fsgofer/filter/filter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go index 9c4864cf1..e03bb7752 100644 --- a/runsc/fsgofer/fsgofer.go +++ b/runsc/fsgofer/fsgofer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/fsgofer_test.go b/runsc/fsgofer/fsgofer_test.go index a500a2976..48860f952 100644 --- a/runsc/fsgofer/fsgofer_test.go +++ b/runsc/fsgofer/fsgofer_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/fsgofer/fsgofer_unsafe.go b/runsc/fsgofer/fsgofer_unsafe.go index e676809ac..99bc25ec1 100644 --- a/runsc/fsgofer/fsgofer_unsafe.go +++ b/runsc/fsgofer/fsgofer_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/main.go b/runsc/main.go index 62b1f01b3..4a92db7c0 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/sandbox/chroot.go b/runsc/sandbox/chroot.go index 35b19a0b1..354049871 100644 --- a/runsc/sandbox/chroot.go +++ b/runsc/sandbox/chroot.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index 86a52c6ae..52fe8fc0f 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 923a52f7f..0fe85cfe1 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go index 00293d45b..73fab13e1 100644 --- a/runsc/specutils/namespace.go +++ b/runsc/specutils/namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index b29802fde..ab14ed1fc 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go index 64e2172c8..b61f1ca62 100644 --- a/runsc/specutils/specutils_test.go +++ b/runsc/specutils/specutils_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/image/image.go b/runsc/test/image/image.go index 069d08013..bcb6f876f 100644 --- a/runsc/test/image/image.go +++ b/runsc/test/image/image.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/image/image_test.go b/runsc/test/image/image_test.go index d89d80a86..763152b47 100644 --- a/runsc/test/image/image_test.go +++ b/runsc/test/image/image_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/image/mysql.sql b/runsc/test/image/mysql.sql index dd5bfaa4e..c1271e719 100644 --- a/runsc/test/image/mysql.sql +++ b/runsc/test/image/mysql.sql @@ -1,4 +1,4 @@ -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/runsc/test/image/ruby.rb b/runsc/test/image/ruby.rb index ae5de3419..25d1ac129 100644 --- a/runsc/test/image/ruby.rb +++ b/runsc/test/image/ruby.rb @@ -1,4 +1,4 @@ -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/runsc/test/image/ruby.sh b/runsc/test/image/ruby.sh index 54be2c931..d3a9b5656 100644 --- a/runsc/test/image/ruby.sh +++ b/runsc/test/image/ruby.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/runsc/test/install.sh b/runsc/test/install.sh index c239588d4..32e1e884e 100755 --- a/runsc/test/install.sh +++ b/runsc/test/install.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/runsc/test/integration/exec_test.go b/runsc/test/integration/exec_test.go index 3cac674d0..fac8337f4 100644 --- a/runsc/test/integration/exec_test.go +++ b/runsc/test/integration/exec_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/integration/integration.go b/runsc/test/integration/integration.go index 49c3c893a..e15321c87 100644 --- a/runsc/test/integration/integration.go +++ b/runsc/test/integration/integration.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/integration/integration_test.go b/runsc/test/integration/integration_test.go index 536bb17e0..526b3a7a1 100644 --- a/runsc/test/integration/integration_test.go +++ b/runsc/test/integration/integration_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/root/cgroup_test.go b/runsc/test/root/cgroup_test.go index 5cb4b794f..fdb94ff64 100644 --- a/runsc/test/root/cgroup_test.go +++ b/runsc/test/root/cgroup_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/root/chroot_test.go b/runsc/test/root/chroot_test.go index 8831e6a78..0ffaaf87b 100644 --- a/runsc/test/root/chroot_test.go +++ b/runsc/test/root/chroot_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/root/root.go b/runsc/test/root/root.go index 790f62c29..586ea0fe3 100644 --- a/runsc/test/root/root.go +++ b/runsc/test/root/root.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/testutil/docker.go b/runsc/test/testutil/docker.go index 7d6a72e5f..3f74e0770 100644 --- a/runsc/test/testutil/docker.go +++ b/runsc/test/testutil/docker.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 4d7ac3bc9..1b5a02c0f 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/test/testutil/testutil_race.go b/runsc/test/testutil/testutil_race.go index 59cfdaa7b..9267af150 100644 --- a/runsc/test/testutil/testutil_race.go +++ b/runsc/test/testutil/testutil_race.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/runsc/tools/dockercfg/dockercfg.go b/runsc/tools/dockercfg/dockercfg.go index 0bd6cad93..110a581ff 100644 --- a/runsc/tools/dockercfg/dockercfg.go +++ b/runsc/tools/dockercfg/dockercfg.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics.go b/tools/go_generics/generics.go index cc61a7537..eaf5c4970 100644 --- a/tools/go_generics/generics.go +++ b/tools/go_generics/generics.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/all_stmts/input.go b/tools/go_generics/generics_tests/all_stmts/input.go index 870af3b6c..19184a3fe 100644 --- a/tools/go_generics/generics_tests/all_stmts/input.go +++ b/tools/go_generics/generics_tests/all_stmts/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/all_stmts/output/output.go b/tools/go_generics/generics_tests/all_stmts/output/output.go index e4e670bf1..51582346c 100644 --- a/tools/go_generics/generics_tests/all_stmts/output/output.go +++ b/tools/go_generics/generics_tests/all_stmts/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/all_types/input.go b/tools/go_generics/generics_tests/all_types/input.go index 3a8643e3d..ed6e97c29 100644 --- a/tools/go_generics/generics_tests/all_types/input.go +++ b/tools/go_generics/generics_tests/all_types/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/all_types/lib/lib.go b/tools/go_generics/generics_tests/all_types/lib/lib.go index d3911d12d..7e73e678e 100644 --- a/tools/go_generics/generics_tests/all_types/lib/lib.go +++ b/tools/go_generics/generics_tests/all_types/lib/lib.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/all_types/output/output.go b/tools/go_generics/generics_tests/all_types/output/output.go index b89840936..ec09a6be4 100644 --- a/tools/go_generics/generics_tests/all_types/output/output.go +++ b/tools/go_generics/generics_tests/all_types/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/consts/input.go b/tools/go_generics/generics_tests/consts/input.go index dabf76e1e..394bcc262 100644 --- a/tools/go_generics/generics_tests/consts/input.go +++ b/tools/go_generics/generics_tests/consts/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/consts/output/output.go b/tools/go_generics/generics_tests/consts/output/output.go index 72865607e..91a07fdc2 100644 --- a/tools/go_generics/generics_tests/consts/output/output.go +++ b/tools/go_generics/generics_tests/consts/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/imports/input.go b/tools/go_generics/generics_tests/imports/input.go index 66b43fee5..22e6641a6 100644 --- a/tools/go_generics/generics_tests/imports/input.go +++ b/tools/go_generics/generics_tests/imports/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/imports/output/output.go b/tools/go_generics/generics_tests/imports/output/output.go index 5f20d43ce..2555c0004 100644 --- a/tools/go_generics/generics_tests/imports/output/output.go +++ b/tools/go_generics/generics_tests/imports/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/remove_typedef/input.go b/tools/go_generics/generics_tests/remove_typedef/input.go index c02307d32..d9c9b8530 100644 --- a/tools/go_generics/generics_tests/remove_typedef/input.go +++ b/tools/go_generics/generics_tests/remove_typedef/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/remove_typedef/output/output.go b/tools/go_generics/generics_tests/remove_typedef/output/output.go index d20a89abd..f111a9426 100644 --- a/tools/go_generics/generics_tests/remove_typedef/output/output.go +++ b/tools/go_generics/generics_tests/remove_typedef/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/simple/input.go b/tools/go_generics/generics_tests/simple/input.go index 670161d6e..711687cf5 100644 --- a/tools/go_generics/generics_tests/simple/input.go +++ b/tools/go_generics/generics_tests/simple/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/generics_tests/simple/output/output.go b/tools/go_generics/generics_tests/simple/output/output.go index 75b5467cd..139c9bf9d 100644 --- a/tools/go_generics/generics_tests/simple/output/output.go +++ b/tools/go_generics/generics_tests/simple/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/globals/globals_visitor.go b/tools/go_generics/globals/globals_visitor.go index fc0de4381..daaa17b1d 100644 --- a/tools/go_generics/globals/globals_visitor.go +++ b/tools/go_generics/globals/globals_visitor.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/globals/scope.go b/tools/go_generics/globals/scope.go index 18743bdee..b75a91689 100644 --- a/tools/go_generics/globals/scope.go +++ b/tools/go_generics/globals/scope.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/go_generics_unittest.sh b/tools/go_generics/go_generics_unittest.sh index 699e1f631..e7553a071 100755 --- a/tools/go_generics/go_generics_unittest.sh +++ b/tools/go_generics/go_generics_unittest.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tools/go_generics/imports.go b/tools/go_generics/imports.go index 97267098b..57f7c3dce 100644 --- a/tools/go_generics/imports.go +++ b/tools/go_generics/imports.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/merge.go b/tools/go_generics/merge.go index ebe7cf4e4..2f83facf8 100644 --- a/tools/go_generics/merge.go +++ b/tools/go_generics/merge.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/remove.go b/tools/go_generics/remove.go index 2a66de762..139d03955 100644 --- a/tools/go_generics/remove.go +++ b/tools/go_generics/remove.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/rules_tests/template.go b/tools/go_generics/rules_tests/template.go index 73c024f0e..f3f31ae8e 100644 --- a/tools/go_generics/rules_tests/template.go +++ b/tools/go_generics/rules_tests/template.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_generics/rules_tests/template_test.go b/tools/go_generics/rules_tests/template_test.go index 76c4cdb64..3a38c8629 100644 --- a/tools/go_generics/rules_tests/template_test.go +++ b/tools/go_generics/rules_tests/template_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/go_stateify/main.go b/tools/go_stateify/main.go index 5646b879a..9e2c8e106 100644 --- a/tools/go_stateify/main.go +++ b/tools/go_stateify/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/workspace_status.sh b/tools/workspace_status.sh index d89db1f99..7d44dad37 100755 --- a/tools/workspace_status.sh +++ b/tools/workspace_status.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/vdso/barrier.h b/vdso/barrier.h index db8185b2e..7866af414 100644 --- a/vdso/barrier.h +++ b/vdso/barrier.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vdso/check_vdso.py b/vdso/check_vdso.py index 9a3142ab8..6f7d7e7ec 100644 --- a/vdso/check_vdso.py +++ b/vdso/check_vdso.py @@ -1,4 +1,4 @@ -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/vdso/compiler.h b/vdso/compiler.h index a661516c3..d65f148fb 100644 --- a/vdso/compiler.h +++ b/vdso/compiler.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vdso/cycle_clock.h b/vdso/cycle_clock.h index 93c5f2c0d..dfb5b427d 100644 --- a/vdso/cycle_clock.h +++ b/vdso/cycle_clock.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vdso/seqlock.h b/vdso/seqlock.h index b527bdbca..ab2f3fda3 100644 --- a/vdso/seqlock.h +++ b/vdso/seqlock.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vdso/syscalls.h b/vdso/syscalls.h index fd79c4642..0be8a7f9b 100644 --- a/vdso/syscalls.h +++ b/vdso/syscalls.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vdso/vdso.cc b/vdso/vdso.cc index db3bdef01..f30dc26a2 100644 --- a/vdso/vdso.cc +++ b/vdso/vdso.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vdso/vdso_time.cc b/vdso/vdso_time.cc index 5d5c8de65..a59771bff 100644 --- a/vdso/vdso_time.cc +++ b/vdso/vdso_time.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vdso/vdso_time.h b/vdso/vdso_time.h index 71d6e2f64..464dadff2 100644 --- a/vdso/vdso_time.h +++ b/vdso/vdso_time.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. -- cgit v1.2.3 From 75cd70ecc9abfd5daaefea04da5070a0e0d620dd Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Tue, 23 Oct 2018 00:19:11 -0700 Subject: Track paths and provide a rename hook. This change also adds extensive testing to the p9 package via mocks. The sanity checks and type checks are moved from the gofer into the core package, where they can be more easily validated. PiperOrigin-RevId: 218296768 Change-Id: I4fc3c326e7bf1e0e140a454cbacbcc6fd617ab55 --- WORKSPACE | 20 +- pkg/amutex/BUILD | 4 +- pkg/atomicbitops/BUILD | 4 +- pkg/binary/BUILD | 4 +- pkg/bits/BUILD | 3 +- pkg/compressio/BUILD | 4 +- pkg/control/client/BUILD | 4 +- pkg/control/server/BUILD | 4 +- pkg/dhcp/BUILD | 4 +- pkg/eventchannel/BUILD | 4 +- pkg/fd/BUILD | 4 +- pkg/gate/BUILD | 4 +- pkg/ilist/BUILD | 4 +- pkg/linewriter/BUILD | 4 +- pkg/log/BUILD | 4 +- pkg/metric/BUILD | 4 +- pkg/p9/BUILD | 2 + pkg/p9/buffer_test.go | 31 + pkg/p9/client.go | 6 + pkg/p9/client_file.go | 4 +- pkg/p9/file.go | 151 +- pkg/p9/handlers.go | 697 ++++++-- pkg/p9/local_server/BUILD | 4 +- pkg/p9/local_server/local_server.go | 5 + pkg/p9/messages_test.go | 37 + pkg/p9/p9.go | 24 + pkg/p9/p9test/BUILD | 76 +- pkg/p9/p9test/client_test.go | 2263 ++++++++++++++++++++++--- pkg/p9/p9test/mocks.go | 489 ------ pkg/p9/p9test/p9test.go | 329 ++++ pkg/p9/path_tree.go | 109 ++ pkg/p9/server.go | 228 ++- pkg/p9/transport.go | 10 +- pkg/rand/BUILD | 4 +- pkg/seccomp/BUILD | 4 +- pkg/secio/BUILD | 4 +- pkg/sentry/arch/BUILD | 3 +- pkg/sentry/context/BUILD | 4 +- pkg/sentry/control/BUILD | 4 +- pkg/sentry/device/BUILD | 4 +- pkg/sentry/fs/anon/BUILD | 4 +- pkg/sentry/fs/gofer/BUILD | 4 - pkg/sentry/fs/gofer/context_file.go | 7 - pkg/sentry/fs/gofer/gofer_test.go | 894 ++-------- pkg/sentry/fs/gofer/session.go | 9 +- pkg/sentry/fs/gofer/session_state.go | 4 +- pkg/sentry/fs/proc/device/BUILD | 4 +- pkg/sentry/hostcpu/BUILD | 4 +- pkg/sentry/kernel/kdefs/BUILD | 4 +- pkg/sentry/kernel/memevent/BUILD | 4 +- pkg/sentry/kernel/sched/BUILD | 4 +- pkg/sentry/loader/BUILD | 3 +- pkg/sentry/memutil/BUILD | 4 +- pkg/sentry/platform/interrupt/BUILD | 4 +- pkg/sentry/platform/kvm/BUILD | 3 +- pkg/sentry/platform/kvm/testutil/BUILD | 4 +- pkg/sentry/platform/procid/BUILD | 4 +- pkg/sentry/platform/ptrace/BUILD | 4 +- pkg/sentry/platform/ring0/BUILD | 3 +- pkg/sentry/platform/ring0/gen_offsets/BUILD | 3 +- pkg/sentry/platform/ring0/pagetables/BUILD | 3 +- pkg/sentry/platform/safecopy/BUILD | 4 +- pkg/sentry/safemem/BUILD | 4 +- pkg/sentry/sighandling/BUILD | 4 +- pkg/sentry/socket/rpcinet/BUILD | 4 +- pkg/sentry/socket/rpcinet/conn/BUILD | 4 +- pkg/sentry/socket/rpcinet/notifier/BUILD | 4 +- pkg/sentry/state/BUILD | 4 +- pkg/sentry/strace/BUILD | 4 +- pkg/sentry/syscalls/BUILD | 4 +- pkg/sentry/time/BUILD | 3 +- pkg/sentry/unimpl/BUILD | 4 +- pkg/sentry/uniqueid/BUILD | 4 +- pkg/sentry/watchdog/BUILD | 4 +- pkg/sleep/BUILD | 4 +- pkg/state/BUILD | 5 +- pkg/state/statefile/BUILD | 4 +- pkg/sync/atomicptrtest/BUILD | 3 +- pkg/sync/seqatomictest/BUILD | 3 +- pkg/syserr/BUILD | 4 +- pkg/syserror/BUILD | 4 +- pkg/tcpip/adapters/gonet/BUILD | 4 +- pkg/tcpip/checker/BUILD | 4 +- pkg/tcpip/link/channel/BUILD | 4 +- pkg/tcpip/link/fdbased/BUILD | 4 +- pkg/tcpip/link/loopback/BUILD | 4 +- pkg/tcpip/link/rawfile/BUILD | 4 +- pkg/tcpip/link/sharedmem/BUILD | 4 +- pkg/tcpip/link/sharedmem/pipe/BUILD | 4 +- pkg/tcpip/link/sharedmem/queue/BUILD | 4 +- pkg/tcpip/link/sniffer/BUILD | 4 +- pkg/tcpip/link/tun/BUILD | 4 +- pkg/tcpip/link/waitable/BUILD | 4 +- pkg/tcpip/network/BUILD | 4 +- pkg/tcpip/network/arp/BUILD | 4 +- pkg/tcpip/network/hash/BUILD | 4 +- pkg/tcpip/network/ipv4/BUILD | 4 +- pkg/tcpip/network/ipv6/BUILD | 4 +- pkg/tcpip/ports/BUILD | 4 +- pkg/tcpip/sample/tun_tcp_connect/BUILD | 4 +- pkg/tcpip/sample/tun_tcp_echo/BUILD | 4 +- pkg/tcpip/transport/tcp/testing/context/BUILD | 4 +- pkg/tcpip/transport/tcpconntrack/BUILD | 4 +- pkg/tmutex/BUILD | 4 +- pkg/unet/BUILD | 4 +- pkg/urpc/BUILD | 4 +- pkg/waiter/fdnotifier/BUILD | 4 +- runsc/boot/BUILD | 4 +- runsc/boot/filter/BUILD | 4 +- runsc/cgroup/BUILD | 4 +- runsc/cmd/BUILD | 4 +- runsc/console/BUILD | 4 +- runsc/container/BUILD | 4 +- runsc/fsgofer/BUILD | 4 +- runsc/fsgofer/filter/BUILD | 4 +- runsc/fsgofer/fsgofer.go | 98 +- runsc/fsgofer/fsgofer_test.go | 78 +- runsc/sandbox/BUILD | 4 +- runsc/specutils/BUILD | 4 +- runsc/test/image/BUILD | 4 +- runsc/test/integration/BUILD | 4 +- runsc/test/root/BUILD | 4 +- runsc/test/testutil/BUILD | 4 +- runsc/tools/dockercfg/BUILD | 4 +- tools/go_generics/BUILD | 4 +- tools/go_generics/globals/BUILD | 4 +- tools/go_generics/rules_tests/BUILD | 3 +- tools/go_stateify/BUILD | 4 +- 128 files changed, 3834 insertions(+), 2147 deletions(-) create mode 100644 pkg/p9/buffer_test.go delete mode 100644 pkg/p9/p9test/mocks.go create mode 100644 pkg/p9/p9test/p9test.go create mode 100644 pkg/p9/path_tree.go (limited to 'runsc/specutils') diff --git a/WORKSPACE b/WORKSPACE index 48e0d3436..841a23e06 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -15,7 +15,7 @@ go_register_toolchains(go_version="1.11.1") load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") gazelle_dependencies() -# Add dependencies on external repositories. +# External repositories, in sorted order. go_repository( name = "com_github_cenkalti_backoff", importpath = "github.com/cenkalti/backoff", @@ -28,6 +28,12 @@ go_repository( commit = "886344bea0798d02ff3fae16a922be5f6b26cee0" ) +go_repository( + name = "com_github_golang_mock", + importpath = "github.com/golang/mock", + commit = "600781dde9cca80734169b9e969d9054ccc57937", +) + go_repository( name = "com_github_google_go-cmp", importpath = "github.com/google/go-cmp", @@ -58,6 +64,12 @@ go_repository( commit = "b2d941ef6a780da2d9982c1fb28d77ad97f54fc7", ) +go_repository( + name = "com_github_syndtr_gocapability", + importpath = "github.com/syndtr/gocapability", + commit = "d98352740cb2c55f81556b63d4a1ec64c5a319c2", +) + go_repository( name = "com_github_vishvananda_netlink", importpath = "github.com/vishvananda/netlink", @@ -81,9 +93,3 @@ go_repository( importpath = "golang.org/x/sys", commit = "0dd5e194bbf5eb84a39666eb4c98a4d007e4203a", ) - -go_repository( - name = "com_github_syndtr_gocapability", - importpath = "github.com/syndtr/gocapability", - commit = "d98352740cb2c55f81556b63d4a1ec64c5a319c2", -) diff --git a/pkg/amutex/BUILD b/pkg/amutex/BUILD index 84e6b79a5..815ee3a69 100644 --- a/pkg/amutex/BUILD +++ b/pkg/amutex/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "amutex", srcs = ["amutex.go"], diff --git a/pkg/atomicbitops/BUILD b/pkg/atomicbitops/BUILD index a8dd17825..235188531 100644 --- a/pkg/atomicbitops/BUILD +++ b/pkg/atomicbitops/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "atomicbitops", srcs = [ diff --git a/pkg/binary/BUILD b/pkg/binary/BUILD index 586d05634..571151f72 100644 --- a/pkg/binary/BUILD +++ b/pkg/binary/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "binary", srcs = ["binary.go"], diff --git a/pkg/bits/BUILD b/pkg/bits/BUILD index 8c943b615..46794bdb8 100644 --- a/pkg/bits/BUILD +++ b/pkg/bits/BUILD @@ -1,6 +1,7 @@ +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") go_library( diff --git a/pkg/compressio/BUILD b/pkg/compressio/BUILD index d70f982c1..72952d735 100644 --- a/pkg/compressio/BUILD +++ b/pkg/compressio/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "compressio", srcs = ["compressio.go"], diff --git a/pkg/control/client/BUILD b/pkg/control/client/BUILD index d58cd1b71..32853875d 100644 --- a/pkg/control/client/BUILD +++ b/pkg/control/client/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "client", srcs = [ diff --git a/pkg/control/server/BUILD b/pkg/control/server/BUILD index c3f74a532..ba2b1be9f 100644 --- a/pkg/control/server/BUILD +++ b/pkg/control/server/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "server", srcs = ["server.go"], diff --git a/pkg/dhcp/BUILD b/pkg/dhcp/BUILD index 711a72c99..c97dfc14b 100644 --- a/pkg/dhcp/BUILD +++ b/pkg/dhcp/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "dhcp", srcs = [ diff --git a/pkg/eventchannel/BUILD b/pkg/eventchannel/BUILD index 9d531ce12..18348ef54 100644 --- a/pkg/eventchannel/BUILD +++ b/pkg/eventchannel/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "eventchannel", srcs = [ diff --git a/pkg/fd/BUILD b/pkg/fd/BUILD index 435b6fa34..06cfd445e 100644 --- a/pkg/fd/BUILD +++ b/pkg/fd/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "fd", srcs = ["fd.go"], diff --git a/pkg/gate/BUILD b/pkg/gate/BUILD index 872eff531..9a87a3a31 100644 --- a/pkg/gate/BUILD +++ b/pkg/gate/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "gate", srcs = [ diff --git a/pkg/ilist/BUILD b/pkg/ilist/BUILD index 1bd71b800..a67aa2cff 100644 --- a/pkg/ilist/BUILD +++ b/pkg/ilist/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "ilist", srcs = [ diff --git a/pkg/linewriter/BUILD b/pkg/linewriter/BUILD index 6c3795432..3f28ba867 100644 --- a/pkg/linewriter/BUILD +++ b/pkg/linewriter/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "linewriter", srcs = ["linewriter.go"], diff --git a/pkg/log/BUILD b/pkg/log/BUILD index fc9281079..bf85b4494 100644 --- a/pkg/log/BUILD +++ b/pkg/log/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "log", srcs = [ diff --git a/pkg/metric/BUILD b/pkg/metric/BUILD index c0cd40c7b..d96e5563b 100644 --- a/pkg/metric/BUILD +++ b/pkg/metric/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "metric", srcs = ["metric.go"], diff --git a/pkg/p9/BUILD b/pkg/p9/BUILD index 1cf5c6458..2c224e65b 100644 --- a/pkg/p9/BUILD +++ b/pkg/p9/BUILD @@ -15,6 +15,7 @@ go_library( "handlers.go", "messages.go", "p9.go", + "path_tree.go", "pool.go", "server.go", "transport.go", @@ -32,6 +33,7 @@ go_test( name = "p9_test", size = "small", srcs = [ + "buffer_test.go", "client_test.go", "messages_test.go", "p9_test.go", diff --git a/pkg/p9/buffer_test.go b/pkg/p9/buffer_test.go new file mode 100644 index 000000000..97eceefa7 --- /dev/null +++ b/pkg/p9/buffer_test.go @@ -0,0 +1,31 @@ +// Copyright 2018 Google Inc. +// +// 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 p9 + +import ( + "testing" +) + +func TestBufferOverrun(t *testing.T) { + buf := &buffer{ + // This header indicates that a large string should follow, but + // it is only two bytes. Reading a string should cause an + // overrun. + data: []byte{0x0, 0x16}, + } + if s := buf.ReadString(); s != "" { + t.Errorf("overrun read got %s, want empty", s) + } +} diff --git a/pkg/p9/client.go b/pkg/p9/client.go index 3ebfab82a..67887874a 100644 --- a/pkg/p9/client.go +++ b/pkg/p9/client.go @@ -116,6 +116,7 @@ func NewClient(socket *unet.Socket, messageSize uint32, version string) (*Client msize: largestFixedSize, } } + // Compute a payload size and round to 512 (normal block size) // if it's larger than a single block. payloadSize := messageSize - largestFixedSize @@ -299,3 +300,8 @@ func (c *Client) sendRecv(t message, r message) error { func (c *Client) Version() uint32 { return c.version } + +// Close closes the underlying socket. +func (c *Client) Close() error { + return c.socket.Close() +} diff --git a/pkg/p9/client_file.go b/pkg/p9/client_file.go index 066639fda..992d1daf7 100644 --- a/pkg/p9/client_file.go +++ b/pkg/p9/client_file.go @@ -172,6 +172,9 @@ func (c *clientFile) SetAttr(valid SetAttrMask, attr SetAttr) error { } // Remove implements File.Remove. +// +// N.B. This method is no longer part of the file interface and should be +// considered deprecated. func (c *clientFile) Remove() error { // Avoid double close. if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) { @@ -181,7 +184,6 @@ func (c *clientFile) Remove() error { // Send the remove message. if err := c.client.sendRecv(&Tremove{FID: c.fid}, &Rremove{}); err != nil { - log.Warningf("Tremove failed, losing FID %v: %v", c.fid, err) return err } diff --git a/pkg/p9/file.go b/pkg/p9/file.go index d2e89e373..55ceb52e1 100644 --- a/pkg/p9/file.go +++ b/pkg/p9/file.go @@ -31,35 +31,63 @@ type Attacher interface { // File is a set of operations corresponding to a single node. // -// Functions below MUST return syscall.Errno values. -// TODO: Enforce that with the type. +// Note that on the server side, the server logic places constraints on +// concurrent operations to make things easier. This may reduce the need for +// complex, error-prone locking and logic in the backend. These are documented +// for each method. // -// These must be implemented in all circumstances. +// There are three different types of guarantees provided: +// +// none: There is no concurrency guarantee. The method may be invoked +// concurrently with any other method on any other file. +// +// read: The method is guaranteed to be exclusive of any write or global +// operation that is mutating the state of the directory tree starting at this +// node. For example, this means creating new files, symlinks, directories or +// renaming a directory entry (or renaming in to this target), but the method +// may be called concurrently with other read methods. +// +// write: The method is guaranteed to be exclusive of any read, write or global +// operation that is mutating the state of the directory tree starting at this +// node, as described in read above. There may however, be other write +// operations executing concurrently on other components in the directory tree. +// +// global: The method is guaranteed to be exclusive of any read, write or +// global operation. type File interface { // Walk walks to the path components given in names. // // Walk returns QIDs in the same order that the names were passed in. // // An empty list of arguments should return a copy of the current file. + // + // On the server, Walk has a read concurrency guarantee. Walk(names []string) ([]QID, File, error) + // WalkGetAttr walks to the next file and returns its maximal set of + // attributes. + // + // Server-side p9.Files may return syscall.ENOSYS to indicate that Walk + // and GetAttr should be used separately to satisfy this request. + // + // On the server, WalkGetAttr has a read concurrency guarantee. + WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error) + // StatFS returns information about the file system associated with // this file. + // + // On the server, StatFS has no concurrency guarantee. StatFS() (FSStat, error) // GetAttr returns attributes of this node. + // + // On the server, GetAttr has a read concurrency guarantee. GetAttr(req AttrMask) (QID, AttrMask, Attr, error) // SetAttr sets attributes on this node. - SetAttr(valid SetAttrMask, attr SetAttr) error - - // Remove removes the file. // - // This is deprecated in favor of UnlinkAt below. - Remove() error - - // Rename renames the file. - Rename(directory File, name string) error + // On the server, SetAttr has a write concurrency guarantee. + SetAttr(valid SetAttrMask, attr SetAttr) error // Close is called when all references are dropped on the server side, // and Close should be called by the client to drop all references. @@ -67,65 +95,93 @@ type File interface { // For server-side implementations of Close, the error is ignored. // // Close must be called even when Open has not been called. + // + // On the server, Close has no concurrency guarantee. Close() error - // Open is called prior to using read/write. + // Open must be called prior to using Read, Write or Readdir. Once Open + // is called, some operations, such as Walk, will no longer work. // - // The *fd.FD may be nil. If an *fd.FD is provided, ownership now - // belongs to the caller and the FD must be non-blocking. + // On the client, Open should be called only once. The fd return is + // optional, and may be nil. // - // If Open returns a non-nil *fd.FD, it should do so for all possible - // OpenFlags. If Open returns a nil *fd.FD, it should similarly return - // a nil *fd.FD for all possible OpenFlags. + // On the server, Open has a read concurrency guarantee. If an *fd.FD + // is provided, ownership now belongs to the caller. Open is guaranteed + // to be called only once. // - // This can be assumed to be one-shot only. + // N.B. The server must resolve any lazy paths when open is called. + // After this point, read and write may be called on files with no + // deletion check, so resolving in the data path is not viable. Open(mode OpenFlags) (*fd.FD, QID, uint32, error) - // Read reads from this file. + // Read reads from this file. Open must be called first. // // This may return io.EOF in addition to syscall.Errno values. // - // Preconditions: Open has been called and returned success. + // On the server, ReadAt has a read concurrency guarantee. See Open for + // additional requirements regarding lazy path resolution. ReadAt(p []byte, offset uint64) (int, error) - // Write writes to this file. + // Write writes to this file. Open must be called first. // // This may return io.EOF in addition to syscall.Errno values. // - // Preconditions: Open has been called and returned success. + // On the server, WriteAt has a read concurrency guarantee. See Open + // for additional requirements regarding lazy path resolution. WriteAt(p []byte, offset uint64) (int, error) - // FSync syncs this node. + // FSync syncs this node. Open must be called first. // - // Preconditions: Open has been called and returned success. + // On the server, FSync has a read concurrency guarantee. FSync() error // Create creates a new regular file and opens it according to the - // flags given. + // flags given. This file is already Open. + // + // N.B. On the client, the returned file is a reference to the current + // file, which now represents the created file. This is not the case on + // the server. These semantics are very subtle and can easily lead to + // bugs, but are a consequence of the 9P create operation. // // See p9.File.Open for a description of *fd.FD. + // + // On the server, Create has a write concurrency guarantee. Create(name string, flags OpenFlags, permissions FileMode, uid UID, gid GID) (*fd.FD, File, QID, uint32, error) // Mkdir creates a subdirectory. + // + // On the server, Mkdir has a write concurrency guarantee. Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error) // Symlink makes a new symbolic link. - Symlink(oldname string, newname string, uid UID, gid GID) (QID, error) + // + // On the server, Symlink has a write concurrency guarantee. + Symlink(oldName string, newName string, uid UID, gid GID) (QID, error) // Link makes a new hard link. - Link(target File, newname string) error + // + // On the server, Link has a write concurrency guarantee. + Link(target File, newName string) error // Mknod makes a new device node. + // + // On the server, Mknod has a write concurrency guarantee. Mknod(name string, permissions FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error) + // Rename renames the file. + // + // Rename will never be called on the server, and RenameAt will always + // be used instead. + Rename(newDir File, newName string) error + // RenameAt renames a given file to a new name in a potentially new // directory. // - // oldname must be a name relative to this file, which must be a - // directory. newname is a name relative to newdir. + // oldName must be a name relative to this file, which must be a + // directory. newName is a name relative to newDir. // - // This is deprecated in favor of Rename. - RenameAt(oldname string, newdir File, newname string) error + // On the server, RenameAt has a global concurrency guarantee. + RenameAt(oldName string, newDir File, newName string) error // UnlinkAt the given named file. // @@ -133,16 +189,20 @@ type File interface { // // Flags are implementation-specific (e.g. O_DIRECTORY), but are // generally Linux unlinkat(2) flags. + // + // On the server, UnlinkAt has a write concurrency guarantee. UnlinkAt(name string, flags uint32) error // Readdir reads directory entries. // // This may return io.EOF in addition to syscall.Errno values. // - // Preconditions: Open has been called and returned success. + // On the server, Readdir has a read concurrency guarantee. Readdir(offset uint64, count uint32) ([]Dirent, error) // Readlink reads the link target. + // + // On the server, Readlink has a read concurrency guarantee. Readlink() (string, error) // Flush is called prior to Close. @@ -150,16 +210,11 @@ type File interface { // Whereas Close drops all references to the file, Flush cleans up the // file state. Behavior is implementation-specific. // - // Flush is not related to flush(9p). Flush is an extension to 9P2000.L, + // Flush is not related to flush(9p). Flush is an extension to 9P2000.L, // see version.go. - Flush() error - - // WalkGetAttr walks to the next file and returns its maximal set of - // attributes. // - // Server-side p9.Files may return syscall.ENOSYS to indicate that Walk - // and GetAttr should be used separately to satisfy this request. - WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error) + // On the server, Flush has a read concurrency guarantee. + Flush() error // Connect establishes a new host-socket backed connection with a // socket. A File does not need to be opened before it can be connected @@ -170,8 +225,22 @@ type File interface { // // The returned FD must be non-blocking. // - // flags indicates the requested type of socket. + // Flags indicates the requested type of socket. + // + // On the server, Connect has a read concurrency guarantee. Connect(flags ConnectFlags) (*fd.FD, error) + + // Renamed is called when this node is renamed. + // + // This may not fail. The file will hold a reference to its parent + // within the p9 package, and is therefore safe to use for the lifetime + // of this File (until Close is called). + // + // This method should not be called by clients, who should use the + // relevant Rename methods. (Although the method will be a no-op.) + // + // On the server, Renamed has a global concurrency guarantee. + Renamed(newDir File, newName string) } // DefaultWalkGetAttr implements File.WalkGetAttr to return ENOSYS for server-side Files. diff --git a/pkg/p9/handlers.go b/pkg/p9/handlers.go index 959dff31d..0d7a6138f 100644 --- a/pkg/p9/handlers.go +++ b/pkg/p9/handlers.go @@ -15,6 +15,7 @@ package p9 import ( + "fmt" "io" "os" "path" @@ -22,22 +23,43 @@ import ( "sync/atomic" "syscall" + "gvisor.googlesource.com/gvisor/pkg/fd" "gvisor.googlesource.com/gvisor/pkg/log" ) -// newErr returns a new error message from an error. -func newErr(err error) *Rlerror { +const maximumNameLength = 255 + +// ExtractErrno extracts a syscall.Errno from a error, best effort. +func ExtractErrno(err error) syscall.Errno { + switch err { + case os.ErrNotExist: + return syscall.ENOENT + case os.ErrExist: + return syscall.EEXIST + case os.ErrPermission: + return syscall.EACCES + case os.ErrInvalid: + return syscall.EINVAL + } + + // Attempt to unwrap. switch e := err.(type) { case syscall.Errno: - return &Rlerror{Error: uint32(e)} + return e case *os.PathError: - return newErr(e.Err) + return ExtractErrno(e.Err) case *os.SyscallError: - return newErr(e.Err) - default: - log.Warningf("unknown error: %v", err) - return &Rlerror{Error: uint32(syscall.EIO)} + return ExtractErrno(e.Err) } + + // Default case. + log.Warningf("unknown error: %v", err) + return syscall.EIO +} + +// newErr returns a new error message from an error. +func newErr(err error) *Rlerror { + return &Rlerror{Error: uint32(ExtractErrno(err))} } // handler is implemented for server-handled messages. @@ -85,13 +107,15 @@ func (t *Tflush) handle(cs *connState) message { return &Rflush{} } -// isSafeName returns true iff the name does not contain directory characters. -// -// We permit walks only on safe names and store the sequence of paths used for -// any given walk in each FID. (This is immutable.) We use this to mark -// relevant FIDs as moved when a successful rename occurs. -func isSafeName(name string) bool { - return name != "" && !strings.Contains(name, "/") && name != "." && name != ".." +// checkSafeName validates the name and returns nil or returns an error. +func checkSafeName(name string) error { + if name == "" || strings.Contains(name, "/") || name == "." || name == ".." { + return syscall.EINVAL + } + if len(name) > maximumNameLength { + return syscall.ENAMETOOLONG + } + return nil } // handle implements handler.handle. @@ -110,22 +134,54 @@ func (t *Tremove) handle(cs *connState) message { } defer ref.DecRef() + // Frustratingly, because we can't be guaranteed that a rename is not + // occurring simultaneously with this removal, we need to acquire the + // global rename lock for this kind of remove operation to ensure that + // ref.parent does not change out from underneath us. + // + // This is why Tremove is a bad idea, and clients should generally use + // Tunlinkat. All p9 clients will use Tunlinkat. + err := ref.safelyGlobal(func() error { + // Is this a root? Can't remove that. + if ref.isRoot() { + return syscall.EINVAL + } + + // N.B. this remove operation is permitted, even if the file is open. + // See also rename below for reasoning. + + // Is this file already deleted? + if ref.isDeleted() { + return syscall.EINVAL + } + + // Retrieve the file's proper name. + name := ref.parent.pathNode.nameFor(ref) + + // Attempt the removal. + if err := ref.parent.file.UnlinkAt(name, 0); err != nil { + return err + } + + // Mark all relevant fids as deleted. We don't need to lock any + // individual nodes because we already hold the global lock. + ref.parent.markChildDeleted(name) + return nil + }) + // "The remove request asks the file server both to remove the file // represented by fid and to clunk the fid, even if the remove fails." // // "It is correct to consider remove to be a clunk with the side effect // of removing the file if permissions allow." // https://swtch.com/plan9port/man/man9/remove.html - err := ref.file.Remove() - - // Clunk the FID regardless of Remove error. if !cs.DeleteFID(t.FID) { return newErr(syscall.EBADF) } - if err != nil { return newErr(err) } + return &Rremove{} } @@ -168,9 +224,12 @@ func (t *Tattach) handle(cs *connState) message { // Build a transient reference. root := &fidRef{ + server: cs.server, + parent: nil, file: sf, refs: 1, - walkable: attr.Mode.IsDir(), + mode: attr.Mode.FileType(), + pathNode: &cs.server.pathTree, } defer root.DecRef() @@ -183,20 +242,24 @@ func (t *Tattach) handle(cs *connState) message { // We want the same traversal checks to apply on attach, so always // attach at the root and use the regular walk paths. names := strings.Split(t.Auth.AttachName, "/") - _, target, _, attr, err := doWalk(cs, root, names) + _, newRef, _, attr, err := doWalk(cs, root, names) if err != nil { return newErr(err) } + defer newRef.DecRef() // Insert the FID. - cs.InsertFID(t.FID, &fidRef{ - file: target, - walkable: attr.Mode.IsDir(), - }) - + cs.InsertFID(t.FID, newRef) return &Rattach{} } +// CanOpen returns whether this file open can be opened, read and written to. +// +// This includes everything except symlinks and sockets. +func CanOpen(mode FileMode) bool { + return mode.IsRegular() || mode.IsDir() || mode.IsNamedPipe() || mode.IsBlockDevice() || mode.IsCharacterDevice() +} + // handle implements handler.handle. func (t *Tlopen) handle(cs *connState) message { // Lookup the FID. @@ -210,13 +273,35 @@ func (t *Tlopen) handle(cs *connState) message { defer ref.openedMu.Unlock() // Has it been opened already? - if ref.opened { + if ref.opened || !CanOpen(ref.mode) { return newErr(syscall.EINVAL) } - // Do the open. - osFile, qid, ioUnit, err := ref.file.Open(t.Flags) - if err != nil { + // Are flags valid? + if t.Flags&^OpenFlagsModeMask != 0 { + return newErr(syscall.EINVAL) + } + + // Is this an attempt to open a directory as writable? Don't accept. + if ref.mode.IsDir() && t.Flags != ReadOnly { + return newErr(syscall.EINVAL) + } + + var ( + qid QID + ioUnit uint32 + osFile *fd.FD + ) + if err := ref.safelyRead(func() (err error) { + // Has it been deleted already? + if ref.isDeleted() { + return syscall.EINVAL + } + + // Do the open. + osFile, qid, ioUnit, err = ref.file.Open(t.Flags) + return err + }); err != nil { return newErr(err) } @@ -229,8 +314,8 @@ func (t *Tlopen) handle(cs *connState) message { func (t *Tlcreate) do(cs *connState, uid UID) (*Rlcreate, error) { // Don't allow complex names. - if !isSafeName(t.Name) { - return nil, syscall.EINVAL + if err := checkSafeName(t.Name); err != nil { + return nil, err } // Lookup the FID. @@ -240,20 +325,48 @@ func (t *Tlcreate) do(cs *connState, uid UID) (*Rlcreate, error) { } defer ref.DecRef() - // Do the create. - osFile, nsf, qid, ioUnit, err := ref.file.Create(t.Name, t.OpenFlags, t.Permissions, uid, t.GID) - if err != nil { + var ( + osFile *fd.FD + nsf File + qid QID + ioUnit uint32 + newRef *fidRef + ) + if err := ref.safelyWrite(func() (err error) { + // Don't allow creation from non-directories or deleted directories. + if ref.isDeleted() || !ref.mode.IsDir() { + return syscall.EINVAL + } + + // Not allowed on open directories. + if _, opened := ref.OpenFlags(); opened { + return syscall.EINVAL + } + + // Do the create. + osFile, nsf, qid, ioUnit, err = ref.file.Create(t.Name, t.OpenFlags, t.Permissions, uid, t.GID) + if err != nil { + return err + } + + newRef = &fidRef{ + server: cs.server, + parent: ref, + file: nsf, + opened: true, + openFlags: t.OpenFlags, + mode: ModeRegular, + pathNode: ref.pathNode.pathNodeFor(t.Name), + } + ref.pathNode.addChild(newRef, t.Name) + ref.IncRef() // Acquire parent reference. + return nil + }); err != nil { return nil, err } // Replace the FID reference. - // - // The new file will be opened already. - cs.InsertFID(t.FID, &fidRef{ - file: nsf, - opened: true, - openFlags: t.OpenFlags, - }) + cs.InsertFID(t.FID, newRef) return &Rlcreate{Rlopen: Rlopen{QID: qid, IoUnit: ioUnit, File: osFile}}, nil } @@ -278,8 +391,8 @@ func (t *Tsymlink) handle(cs *connState) message { func (t *Tsymlink) do(cs *connState, uid UID) (*Rsymlink, error) { // Don't allow complex names. - if !isSafeName(t.Name) { - return nil, syscall.EINVAL + if err := checkSafeName(t.Name); err != nil { + return nil, err } // Lookup the FID. @@ -289,9 +402,22 @@ func (t *Tsymlink) do(cs *connState, uid UID) (*Rsymlink, error) { } defer ref.DecRef() - // Do the symlink. - qid, err := ref.file.Symlink(t.Target, t.Name, uid, t.GID) - if err != nil { + var qid QID + if err := ref.safelyWrite(func() (err error) { + // Don't allow symlinks from non-directories or deleted directories. + if ref.isDeleted() || !ref.mode.IsDir() { + return syscall.EINVAL + } + + // Not allowed on open directories. + if _, opened := ref.OpenFlags(); opened { + return syscall.EINVAL + } + + // Do the symlink. + qid, err = ref.file.Symlink(t.Target, t.Name, uid, t.GID) + return err + }); err != nil { return nil, err } @@ -301,8 +427,8 @@ func (t *Tsymlink) do(cs *connState, uid UID) (*Rsymlink, error) { // handle implements handler.handle. func (t *Tlink) handle(cs *connState) message { // Don't allow complex names. - if !isSafeName(t.Name) { - return newErr(syscall.EINVAL) + if err := checkSafeName(t.Name); err != nil { + return newErr(err) } // Lookup the FID. @@ -319,8 +445,20 @@ func (t *Tlink) handle(cs *connState) message { } defer refTarget.DecRef() - // Do the link. - if err := ref.file.Link(refTarget.file, t.Name); err != nil { + if err := ref.safelyWrite(func() (err error) { + // Don't allow create links from non-directories or deleted directories. + if ref.isDeleted() || !ref.mode.IsDir() { + return syscall.EINVAL + } + + // Not allowed on open directories. + if _, opened := ref.OpenFlags(); opened { + return syscall.EINVAL + } + + // Do the link. + return ref.file.Link(refTarget.file, t.Name) + }); err != nil { return newErr(err) } @@ -330,8 +468,11 @@ func (t *Tlink) handle(cs *connState) message { // handle implements handler.handle. func (t *Trenameat) handle(cs *connState) message { // Don't allow complex names. - if !isSafeName(t.OldName) || !isSafeName(t.NewName) { - return newErr(syscall.EINVAL) + if err := checkSafeName(t.OldName); err != nil { + return newErr(err) + } + if err := checkSafeName(t.NewName); err != nil { + return newErr(err) } // Lookup the FID. @@ -348,8 +489,32 @@ func (t *Trenameat) handle(cs *connState) message { } defer refTarget.DecRef() - // Do the rename. - if err := ref.file.RenameAt(t.OldName, refTarget.file, t.NewName); err != nil { + // Perform the rename holding the global lock. + if err := ref.safelyGlobal(func() (err error) { + // Don't allow renaming across deleted directories. + if ref.isDeleted() || !ref.mode.IsDir() || refTarget.isDeleted() || !refTarget.mode.IsDir() { + return syscall.EINVAL + } + + // Not allowed on open directories. + if _, opened := ref.OpenFlags(); opened { + return syscall.EINVAL + } + + // Is this the same file? If yes, short-circuit and return success. + if ref.pathNode == refTarget.pathNode && t.OldName == t.NewName { + return nil + } + + // Attempt the actual rename. + if err := ref.file.RenameAt(t.OldName, refTarget.file, t.NewName); err != nil { + return err + } + + // Update the path tree. + ref.renameChildTo(t.OldName, refTarget, t.NewName) + return nil + }); err != nil { return newErr(err) } @@ -359,8 +524,8 @@ func (t *Trenameat) handle(cs *connState) message { // handle implements handler.handle. func (t *Tunlinkat) handle(cs *connState) message { // Don't allow complex names. - if !isSafeName(t.Name) { - return newErr(syscall.EINVAL) + if err := checkSafeName(t.Name); err != nil { + return newErr(err) } // Lookup the FID. @@ -370,8 +535,40 @@ func (t *Tunlinkat) handle(cs *connState) message { } defer ref.DecRef() - // Do the unlink. - if err := ref.file.UnlinkAt(t.Name, t.Flags); err != nil { + if err := ref.safelyWrite(func() (err error) { + // Don't allow deletion from non-directories or deleted directories. + if ref.isDeleted() || !ref.mode.IsDir() { + return syscall.EINVAL + } + + // Not allowed on open directories. + if _, opened := ref.OpenFlags(); opened { + return syscall.EINVAL + } + + // Before we do the unlink itself, we need to ensure that there + // are no operations in flight on associated path node. The + // child's path node lock must be held to ensure that the + // unlink at marking the child deleted below is atomic with + // respect to any other read or write operations. + // + // This is one case where we have a lock ordering issue, but + // since we always acquire deeper in the hierarchy, we know + // that we are free of lock cycles. + childPathNode := ref.pathNode.pathNodeFor(t.Name) + childPathNode.mu.Lock() + defer childPathNode.mu.Unlock() + + // Do the unlink. + err = ref.file.UnlinkAt(t.Name, t.Flags) + if err != nil { + return err + } + + // Mark the path as deleted. + ref.markChildDeleted(t.Name) + return nil + }); err != nil { return newErr(err) } @@ -381,8 +578,8 @@ func (t *Tunlinkat) handle(cs *connState) message { // handle implements handler.handle. func (t *Trename) handle(cs *connState) message { // Don't allow complex names. - if !isSafeName(t.Name) { - return newErr(syscall.EINVAL) + if err := checkSafeName(t.Name); err != nil { + return newErr(err) } // Lookup the FID. @@ -399,8 +596,43 @@ func (t *Trename) handle(cs *connState) message { } defer refTarget.DecRef() - // Call the rename method. - if err := ref.file.Rename(refTarget.file, t.Name); err != nil { + if err := ref.safelyGlobal(func() (err error) { + // Don't allow a root rename. + if ref.isRoot() { + return syscall.EINVAL + } + + // Don't allow renaming deleting entries, or target non-directories. + if ref.isDeleted() || refTarget.isDeleted() || !refTarget.mode.IsDir() { + return syscall.EINVAL + } + + // If the parent is deleted, but we not, something is seriously wrong. + // It's fail to die at this point with an assertion failure. + if ref.parent.isDeleted() { + panic(fmt.Sprintf("parent %+v deleted, child %+v is not", ref.parent, ref)) + } + + // N.B. The rename operation is allowed to proceed on open files. It + // does impact the state of its parent, but this is merely a sanity + // check in any case, and the operation is safe. There may be other + // files corresponding to the same path that are renamed anyways. + + // Check for the exact same file and short-circuit. + oldName := ref.parent.pathNode.nameFor(ref) + if ref.parent.pathNode == refTarget.pathNode && oldName == t.Name { + return nil + } + + // Call the rename method on the parent. + if err := ref.parent.file.RenameAt(oldName, refTarget.file, t.Name); err != nil { + return err + } + + // Update the path tree. + ref.parent.renameChildTo(oldName, refTarget, t.Name) + return nil + }); err != nil { return newErr(err) } @@ -416,9 +648,19 @@ func (t *Treadlink) handle(cs *connState) message { } defer ref.DecRef() - // Do the read. - target, err := ref.file.Readlink() - if err != nil { + var target string + if err := ref.safelyRead(func() (err error) { + // Don't allow readlink on deleted files. There is no need to + // check if this file is opened because symlinks cannot be + // opened. + if ref.isDeleted() || !ref.mode.IsSymlink() { + return syscall.EINVAL + } + + // Do the read. + target, err = ref.file.Readlink() + return err + }); err != nil { return newErr(err) } @@ -434,26 +676,30 @@ func (t *Tread) handle(cs *connState) message { } defer ref.DecRef() - // Has it been opened already? - openFlags, opened := ref.OpenFlags() - if !opened { - return newErr(syscall.EINVAL) - } - - // Can it be read? Check permissions. - if openFlags&OpenFlagsModeMask == WriteOnly { - return newErr(syscall.EPERM) - } - // Constrain the size of the read buffer. if int(t.Count) > int(maximumLength) { return newErr(syscall.ENOBUFS) } - // Do the read. - data := make([]byte, t.Count) - n, err := ref.file.ReadAt(data, t.Offset) - if err != nil && err != io.EOF { + var ( + data = make([]byte, t.Count) + n int + ) + if err := ref.safelyRead(func() (err error) { + // Has it been opened already? + openFlags, opened := ref.OpenFlags() + if !opened { + return syscall.EINVAL + } + + // Can it be read? Check permissions. + if openFlags&OpenFlagsModeMask == WriteOnly { + return syscall.EPERM + } + + n, err = ref.file.ReadAt(data, t.Offset) + return err + }); err != nil && err != io.EOF { return newErr(err) } @@ -469,20 +715,22 @@ func (t *Twrite) handle(cs *connState) message { } defer ref.DecRef() - // Has it been opened already? - openFlags, opened := ref.OpenFlags() - if !opened { - return newErr(syscall.EINVAL) - } + var n int + if err := ref.safelyRead(func() (err error) { + // Has it been opened already? + openFlags, opened := ref.OpenFlags() + if !opened { + return syscall.EINVAL + } - // Can it be write? Check permissions. - if openFlags&OpenFlagsModeMask == ReadOnly { - return newErr(syscall.EPERM) - } + // Can it be write? Check permissions. + if openFlags&OpenFlagsModeMask == ReadOnly { + return syscall.EPERM + } - // Do the write. - n, err := ref.file.WriteAt(t.Data, t.Offset) - if err != nil { + n, err = ref.file.WriteAt(t.Data, t.Offset) + return err + }); err != nil { return newErr(err) } @@ -500,8 +748,8 @@ func (t *Tmknod) handle(cs *connState) message { func (t *Tmknod) do(cs *connState, uid UID) (*Rmknod, error) { // Don't allow complex names. - if !isSafeName(t.Name) { - return nil, syscall.EINVAL + if err := checkSafeName(t.Name); err != nil { + return nil, err } // Lookup the FID. @@ -511,9 +759,22 @@ func (t *Tmknod) do(cs *connState, uid UID) (*Rmknod, error) { } defer ref.DecRef() - // Do the mknod. - qid, err := ref.file.Mknod(t.Name, t.Permissions, t.Major, t.Minor, uid, t.GID) - if err != nil { + var qid QID + if err := ref.safelyWrite(func() (err error) { + // Don't allow mknod on deleted files. + if ref.isDeleted() || !ref.mode.IsDir() { + return syscall.EINVAL + } + + // Not allowed on open directories. + if _, opened := ref.OpenFlags(); opened { + return syscall.EINVAL + } + + // Do the mknod. + qid, err = ref.file.Mknod(t.Name, t.Permissions, t.Major, t.Minor, uid, t.GID) + return err + }); err != nil { return nil, err } @@ -531,8 +792,8 @@ func (t *Tmkdir) handle(cs *connState) message { func (t *Tmkdir) do(cs *connState, uid UID) (*Rmkdir, error) { // Don't allow complex names. - if !isSafeName(t.Name) { - return nil, syscall.EINVAL + if err := checkSafeName(t.Name); err != nil { + return nil, err } // Lookup the FID. @@ -542,9 +803,22 @@ func (t *Tmkdir) do(cs *connState, uid UID) (*Rmkdir, error) { } defer ref.DecRef() - // Do the mkdir. - qid, err := ref.file.Mkdir(t.Name, t.Permissions, uid, t.GID) - if err != nil { + var qid QID + if err := ref.safelyWrite(func() (err error) { + // Don't allow mkdir on deleted files. + if ref.isDeleted() || !ref.mode.IsDir() { + return syscall.EINVAL + } + + // Not allowed on open directories. + if _, opened := ref.OpenFlags(); opened { + return syscall.EINVAL + } + + // Do the mkdir. + qid, err = ref.file.Mkdir(t.Name, t.Permissions, uid, t.GID) + return err + }); err != nil { return nil, err } @@ -560,9 +834,20 @@ func (t *Tgetattr) handle(cs *connState) message { } defer ref.DecRef() - // Get attributes. - qid, valid, attr, err := ref.file.GetAttr(t.AttrMask) - if err != nil { + // We allow getattr on deleted files. Depending on the backing + // implementation, it's possible that races exist that might allow + // fetching attributes of other files. But we need to generally allow + // refreshing attributes and this is a minor leak, if at all. + + var ( + qid QID + valid AttrMask + attr Attr + ) + if err := ref.safelyRead(func() (err error) { + qid, valid, attr, err = ref.file.GetAttr(t.AttrMask) + return err + }); err != nil { return newErr(err) } @@ -578,8 +863,18 @@ func (t *Tsetattr) handle(cs *connState) message { } defer ref.DecRef() - // Set attributes. - if err := ref.file.SetAttr(t.Valid, t.SetAttr); err != nil { + if err := ref.safelyWrite(func() error { + // We don't allow setattr on files that have been deleted. + // This might be technically incorrect, as it's possible that + // there were multiple links and you can still change the + // corresponding inode information. + if ref.isDeleted() { + return syscall.EINVAL + } + + // Set the attributes. + return ref.file.SetAttr(t.Valid, t.SetAttr) + }); err != nil { return newErr(err) } @@ -621,14 +916,25 @@ func (t *Treaddir) handle(cs *connState) message { } defer ref.DecRef() - // Has it been opened already? - if _, opened := ref.OpenFlags(); !opened { - return newErr(syscall.EINVAL) - } + var entries []Dirent + if err := ref.safelyRead(func() (err error) { + // Don't allow reading deleted directories. + if ref.isDeleted() || !ref.mode.IsDir() { + return syscall.EINVAL + } + + // Has it been opened already? + if _, opened := ref.OpenFlags(); !opened { + return syscall.EINVAL + } - // Read the entries. - entries, err := ref.file.Readdir(t.Offset, t.Count) - if err != nil && err != io.EOF { + // Read the entries. + entries, err = ref.file.Readdir(t.Offset, t.Count) + if err != nil && err != io.EOF { + return err + } + return nil + }); err != nil { return newErr(err) } @@ -644,13 +950,15 @@ func (t *Tfsync) handle(cs *connState) message { } defer ref.DecRef() - // Has it been opened already? - if _, opened := ref.OpenFlags(); !opened { - return newErr(syscall.EINVAL) - } + if err := ref.safelyRead(func() (err error) { + // Has it been opened already? + if _, opened := ref.OpenFlags(); !opened { + return syscall.EINVAL + } - err := ref.file.FSync() - if err != nil { + // Perform the sync. + return ref.file.FSync() + }); err != nil { return newErr(err) } @@ -671,6 +979,11 @@ func (t *Tstatfs) handle(cs *connState) message { return newErr(err) } + // Constrain the name length. + if st.NameLength > maximumNameLength { + st.NameLength = maximumNameLength + } + return &Rstatfs{st} } @@ -682,7 +995,7 @@ func (t *Tflushf) handle(cs *connState) message { } defer ref.DecRef() - if err := ref.file.Flush(); err != nil { + if err := ref.safelyRead(ref.file.Flush); err != nil { return newErr(err) } @@ -726,12 +1039,14 @@ func walkOne(qids []QID, from File, names []string) ([]QID, File, AttrMask, Attr // doWalk walks from a given fidRef. // -// This enforces that all intermediate nodes are walkable (directories). -func doWalk(cs *connState, ref *fidRef, names []string) (qids []QID, sf File, valid AttrMask, attr Attr, err error) { +// This enforces that all intermediate nodes are walkable (directories). The +// fidRef returned (newRef) has a reference associated with it that is now +// owned by the caller and must be handled appropriately. +func doWalk(cs *connState, ref *fidRef, names []string) (qids []QID, newRef *fidRef, valid AttrMask, attr Attr, err error) { // Check the names. for _, name := range names { - if !isSafeName(name) { - err = syscall.EINVAL + err = checkSafeName(name) + if err != nil { return } } @@ -745,44 +1060,88 @@ func doWalk(cs *connState, ref *fidRef, names []string) (qids []QID, sf File, va // Is this an empty list? Handle specially. We don't actually need to // validate anything since this is always permitted. if len(names) == 0 { - return walkOne(nil, ref.file, nil) - } - - // Is it walkable? - if !ref.walkable { - err = syscall.EINVAL - return + var sf File // Temporary. + if err := ref.maybeParent().safelyRead(func() (err error) { + // Clone the single element. + qids, sf, valid, attr, err = walkOne(nil, ref.file, nil) + if err != nil { + return err + } + + newRef = &fidRef{ + server: cs.server, + parent: ref.parent, + file: sf, + mode: ref.mode, + pathNode: ref.pathNode, + + // For the clone case, the cloned fid must + // preserve the deleted property of the + // original FID. + deleted: ref.deleted, + } + if !ref.isRoot() { + if !newRef.isDeleted() { + // Add only if a non-root node; the same node. + ref.parent.pathNode.addChild(newRef, ref.parent.pathNode.nameFor(ref)) + } + ref.parent.IncRef() // Acquire parent reference. + } + // doWalk returns a reference. + newRef.IncRef() + return nil + }); err != nil { + return nil, nil, AttrMask{}, Attr{}, err + } + return qids, newRef, valid, attr, nil } - from := ref.file // Start at the passed ref. - // Do the walk, one element at a time. + walkRef := ref + walkRef.IncRef() for i := 0; i < len(names); i++ { - qids, sf, valid, attr, err = walkOne(qids, from, names[i:i+1]) - - // Close the intermediate file. Note that we don't close the - // first file because in that case we are walking from the - // existing reference. - if i > 0 { - from.Close() - } - from = sf // Use the new file. - - // Was there an error walking? - if err != nil { - return nil, nil, AttrMask{}, Attr{}, err - } - // We won't allow beyond past symlinks; stop here if this isn't // a proper directory and we have additional paths to walk. - if !valid.Mode || (!attr.Mode.IsDir() && i < len(names)-1) { - from.Close() // Not using the file object. + if !walkRef.mode.IsDir() { + walkRef.DecRef() // Drop walk reference; no lock required. return nil, nil, AttrMask{}, Attr{}, syscall.EINVAL } + + var sf File // Temporary. + if err := walkRef.safelyRead(func() (err error) { + qids, sf, valid, attr, err = walkOne(qids, walkRef.file, names[i:i+1]) + if err != nil { + return err + } + + // Note that we don't need to acquire a lock on any of + // these individual instances. That's because they are + // not actually addressable via a FID. They are + // anonymous. They exist in the tree for tracking + // purposes. + newRef := &fidRef{ + server: cs.server, + parent: walkRef, + file: sf, + mode: attr.Mode.FileType(), + pathNode: walkRef.pathNode.pathNodeFor(names[i]), + } + walkRef.pathNode.addChild(newRef, names[i]) + // We allow our walk reference to become the new parent + // reference here and so we don't IncRef. Instead, just + // set walkRef to the newRef above and acquire a new + // walk reference. + walkRef = newRef + walkRef.IncRef() + return nil + }); err != nil { + walkRef.DecRef() // Drop the old walkRef. + return nil, nil, AttrMask{}, Attr{}, err + } } // Success. - return qids, sf, valid, attr, nil + return qids, walkRef, valid, attr, nil } // handle implements handler.handle. @@ -795,17 +1154,14 @@ func (t *Twalk) handle(cs *connState) message { defer ref.DecRef() // Do the walk. - qids, sf, _, attr, err := doWalk(cs, ref, t.Names) + qids, newRef, _, _, err := doWalk(cs, ref, t.Names) if err != nil { return newErr(err) } + defer newRef.DecRef() // Install the new FID. - cs.InsertFID(t.NewFID, &fidRef{ - file: sf, - walkable: attr.Mode.IsDir(), - }) - + cs.InsertFID(t.NewFID, newRef) return &Rwalk{QIDs: qids} } @@ -819,17 +1175,14 @@ func (t *Twalkgetattr) handle(cs *connState) message { defer ref.DecRef() // Do the walk. - qids, sf, valid, attr, err := doWalk(cs, ref, t.Names) + qids, newRef, valid, attr, err := doWalk(cs, ref, t.Names) if err != nil { return newErr(err) } + defer newRef.DecRef() // Install the new FID. - cs.InsertFID(t.NewFID, &fidRef{ - file: sf, - walkable: attr.Mode.IsDir(), - }) - + cs.InsertFID(t.NewFID, newRef) return &Rwalkgetattr{QIDs: qids, Valid: valid, Attr: attr} } @@ -878,9 +1231,17 @@ func (t *Tlconnect) handle(cs *connState) message { } defer ref.DecRef() - // Do the connect. - osFile, err := ref.file.Connect(t.Flags) - if err != nil { + var osFile *fd.FD + if err := ref.safelyRead(func() (err error) { + // Don't allow connecting to deleted files. + if ref.isDeleted() || !ref.mode.IsSocket() { + return syscall.EINVAL + } + + // Do the connect. + osFile, err = ref.file.Connect(t.Flags) + return err + }); err != nil { return newErr(err) } diff --git a/pkg/p9/local_server/BUILD b/pkg/p9/local_server/BUILD index 8229e6308..b17ebb79d 100644 --- a/pkg/p9/local_server/BUILD +++ b/pkg/p9/local_server/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_binary") +package(licenses = ["notice"]) # Apache 2.0 + go_binary( name = "local_server", srcs = ["local_server.go"], diff --git a/pkg/p9/local_server/local_server.go b/pkg/p9/local_server/local_server.go index 1e6aaa762..69b90c6cd 100644 --- a/pkg/p9/local_server/local_server.go +++ b/pkg/p9/local_server/local_server.go @@ -318,6 +318,11 @@ func (l *local) Connect(p9.ConnectFlags) (*fd.FD, error) { return nil, syscall.ECONNREFUSED } +// Renamed implements p9.File.Renamed. +func (l *local) Renamed(parent p9.File, newName string) { + l.path = path.Join(parent.(*local).path, newName) +} + func main() { log.SetLevel(log.Debug) diff --git a/pkg/p9/messages_test.go b/pkg/p9/messages_test.go index dfb41bb76..c0d65d82c 100644 --- a/pkg/p9/messages_test.go +++ b/pkg/p9/messages_test.go @@ -15,6 +15,7 @@ package p9 import ( + "fmt" "reflect" "testing" ) @@ -186,6 +187,13 @@ func TestEncodeDecode(t *testing.T) { &Rxattrwalk{ Size: 1, }, + &Txattrcreate{ + FID: 1, + Name: "a", + AttrSize: 2, + Flags: 3, + }, + &Rxattrcreate{}, &Treaddir{ Directory: 1, Offset: 2, @@ -389,3 +397,32 @@ func TestEncodeDecode(t *testing.T) { } } } + +func TestMessageStrings(t *testing.T) { + for typ, fn := range messageRegistry { + name := fmt.Sprintf("%+v", typ) + t.Run(name, func(t *testing.T) { + defer func() { // Ensure no panic. + if r := recover(); r != nil { + t.Errorf("printing %s failed: %v", name, r) + } + }() + m := fn() + _ = fmt.Sprintf("%v", m) + err := ErrInvalidMsgType{typ} + _ = err.Error() + }) + } +} + +func TestRegisterDuplicate(t *testing.T) { + defer func() { + if r := recover(); r == nil { + // We expect a panic. + t.FailNow() + } + }() + + // Register a duplicate. + register(&Rlerror{}) +} diff --git a/pkg/p9/p9.go b/pkg/p9/p9.go index 3b0993ecd..be644e7bf 100644 --- a/pkg/p9/p9.go +++ b/pkg/p9/p9.go @@ -984,6 +984,30 @@ func (s *SetAttr) Encode(b *buffer) { b.Write64(s.MTimeNanoSeconds) } +// Apply applies this to the given Attr. +func (a *Attr) Apply(mask SetAttrMask, attr SetAttr) { + if mask.Permissions { + a.Mode = a.Mode&^PermissionsMask | (attr.Permissions & PermissionsMask) + } + if mask.UID { + a.UID = attr.UID + } + if mask.GID { + a.GID = attr.GID + } + if mask.Size { + a.Size = attr.Size + } + if mask.ATime { + a.ATimeSeconds = attr.ATimeSeconds + a.ATimeNanoSeconds = attr.ATimeNanoSeconds + } + if mask.MTime { + a.MTimeSeconds = attr.MTimeSeconds + a.MTimeNanoSeconds = attr.MTimeNanoSeconds + } +} + // Dirent is used for readdir. type Dirent struct { // QID is the entry QID. diff --git a/pkg/p9/p9test/BUILD b/pkg/p9/p9test/BUILD index d6f428e11..7c4b875ce 100644 --- a/pkg/p9/p9test/BUILD +++ b/pkg/p9/p9test/BUILD @@ -1,16 +1,60 @@ +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +alias( + name = "mockgen", + actual = "@com_github_golang_mock//mockgen:mockgen", +) -go_test( - name = "p9test_test", - size = "small", - srcs = ["client_test.go"], - embed = [":p9test"], +MOCK_SRC_PACKAGE = "gvisor.googlesource.com/gvisor/pkg/p9" + +# mockgen_reflect is a source file that contains mock generation code that +# imports the p9 package and generates a specification via reflection. The +# usual generation path must be split into two distinct parts because the full +# source tree is not available to all build targets. Only declared depencies +# are available (and even then, not the Go source files). +genrule( + name = "mockgen_reflect", + testonly = 1, + outs = ["mockgen_reflect.go"], + cmd = ( + "$(location :mockgen) " + + "-package p9test " + + "-prog_only " + MOCK_SRC_PACKAGE + " " + + "Attacher,File > $@" + ), + tools = [":mockgen"], +) + +# mockgen_exec is the binary that includes the above reflection generator. +# Running this binary will emit an encoded version of the p9 Attacher and File +# structures. This is consumed by the mocks genrule, below. +go_binary( + name = "mockgen_exec", + testonly = 1, + srcs = ["mockgen_reflect.go"], deps = [ - "//pkg/fd", "//pkg/p9", - "//pkg/unet", + "@com_github_golang_mock//mockgen/model:go_default_library", + ], +) + +# mocks consumes the encoded output above, and generates the full source for a +# set of mocks. These are included directly in the p9test library. +genrule( + name = "mocks", + testonly = 1, + outs = ["mocks.go"], + cmd = ( + "$(location :mockgen) " + + "-package p9test " + + "-exec_only $(location :mockgen_exec) " + MOCK_SRC_PACKAGE + " File > $@" + ), + tools = [ + ":mockgen", + ":mockgen_exec", ], ) @@ -18,11 +62,27 @@ go_library( name = "p9test", srcs = [ "mocks.go", + "p9test.go", ], importpath = "gvisor.googlesource.com/gvisor/pkg/p9/p9test", visibility = ["//:sandbox"], + deps = [ + "//pkg/fd", + "//pkg/log", + "//pkg/p9", + "//pkg/unet", + "@com_github_golang_mock//gomock:go_default_library", + ], +) + +go_test( + name = "client_test", + size = "small", + srcs = ["client_test.go"], + embed = [":p9test"], deps = [ "//pkg/fd", "//pkg/p9", + "@com_github_golang_mock//gomock:go_default_library", ], ) diff --git a/pkg/p9/p9test/client_test.go b/pkg/p9/p9test/client_test.go index db562b9ba..242d81b95 100644 --- a/pkg/p9/p9test/client_test.go +++ b/pkg/p9/p9test/client_test.go @@ -15,360 +15,2059 @@ package p9test import ( - "io/ioutil" + "bytes" + "fmt" + "io" + "math/rand" "os" "reflect" + "strings" + "sync" "syscall" "testing" + "time" + "github.com/golang/mock/gomock" "gvisor.googlesource.com/gvisor/pkg/fd" "gvisor.googlesource.com/gvisor/pkg/p9" - "gvisor.googlesource.com/gvisor/pkg/unet" ) -func TestDonateFD(t *testing.T) { - // Temporary file. - osFile, err := ioutil.TempFile("", "p9") +func TestPanic(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + // Create a new root. + d := h.NewDirectory(nil)(nil) + defer d.Close() // Needed manually. + h.Attacher.EXPECT().Attach().Return(d, nil).Do(func() { + // Panic here, and ensure that we get back EFAULT. + panic("handler") + }) + + // Attach to the client. + if _, err := c.Attach("/"); err != syscall.EFAULT { + t.Fatalf("got attach err %v, want EFAULT", err) + } +} + +func TestAttachNoLeak(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + // Create a new root. + d := h.NewDirectory(nil)(nil) + h.Attacher.EXPECT().Attach().Return(d, nil).Times(1) + + // Attach to the client. + f, err := c.Attach("/") + if err != nil { + t.Fatalf("got attach err %v, want nil", err) + } + + // Don't close the file. This should be closed automatically when the + // client disconnects. The mock asserts that everything is closed + // exactly once. This statement just removes the unused variable error. + _ = f +} + +func TestBadAttach(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + // Return an error on attach. + h.Attacher.EXPECT().Attach().Return(nil, syscall.EINVAL).Times(1) + + // Attach to the client. + if _, err := c.Attach("/"); err != syscall.EINVAL { + t.Fatalf("got attach err %v, want syscall.EINVAL", err) + } +} + +func TestWalkAttach(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + // Create a new root. + d := h.NewDirectory(map[string]Generator{ + "a": h.NewDirectory(map[string]Generator{ + "b": h.NewFile(), + }), + })(nil) + h.Attacher.EXPECT().Attach().Return(d, nil).Times(1) + + // Attach to the client as a non-root, and ensure that the walk above + // occurs as expected. We should get back b, and all references should + // be dropped when the file is closed. + f, err := c.Attach("/a/b") + if err != nil { + t.Fatalf("got attach err %v, want nil", err) + } + defer f.Close() + + // Check that's a regular file. + if _, _, attr, err := f.GetAttr(p9.AttrMaskAll()); err != nil { + t.Errorf("got err %v, want nil", err) + } else if !attr.Mode.IsRegular() { + t.Errorf("got mode %v, want regular file", err) + } +} + +// newTypeMap returns a new type map dictionary. +func newTypeMap(h *Harness) map[string]Generator { + return map[string]Generator{ + "directory": h.NewDirectory(map[string]Generator{}), + "file": h.NewFile(), + "symlink": h.NewSymlink(), + "block-device": h.NewBlockDevice(), + "character-device": h.NewCharacterDevice(), + "named-pipe": h.NewNamedPipe(), + "socket": h.NewSocket(), + } +} + +// newRoot returns a new root filesystem. +// +// This is set up in a deterministic way for testing most operations. +// +// The represented file system looks like: +// - file +// - symlink +// - directory +// ... +// + one +// - file +// - symlink +// - directory +// ... +// + two +// - file +// - symlink +// - directory +// ... +// + three +// - file +// - symlink +// - directory +// ... +func newRoot(h *Harness, c *p9.Client) (*Mock, p9.File) { + root := newTypeMap(h) + one := newTypeMap(h) + two := newTypeMap(h) + three := newTypeMap(h) + one["two"] = h.NewDirectory(two) // Will be nested in one. + root["one"] = h.NewDirectory(one) // Top level. + root["three"] = h.NewDirectory(three) // Alternate top-level. + + // Create a new root. + rootBackend := h.NewDirectory(root)(nil) + h.Attacher.EXPECT().Attach().Return(rootBackend, nil) + + // Attach to the client. + r, err := c.Attach("/") + if err != nil { + h.t.Fatalf("got attach err %v, want nil", err) + } + + return rootBackend, r +} + +func allInvalidNames(from string) []string { + return []string{ + from + "/other", + from + "/..", + from + "/.", + from + "/", + "other/" + from, + "/" + from, + "./" + from, + "../" + from, + ".", + "..", + "/", + "", + } +} + +func TestWalkInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + // Run relevant tests. + for name := range newTypeMap(h) { + // These are all the various ways that one might attempt to + // construct compound paths. They should all be rejected, as + // any compound that contains a / is not allowed, as well as + // the singular paths of '.' and '..'. + if _, _, err := root.Walk([]string{".", name}); err != syscall.EINVAL { + t.Errorf("Walk through . %s wanted EINVAL, got %v", name, err) + } + if _, _, err := root.Walk([]string{"..", name}); err != syscall.EINVAL { + t.Errorf("Walk through . %s wanted EINVAL, got %v", name, err) + } + if _, _, err := root.Walk([]string{name, "."}); err != syscall.EINVAL { + t.Errorf("Walk through %s . wanted EINVAL, got %v", name, err) + } + if _, _, err := root.Walk([]string{name, ".."}); err != syscall.EINVAL { + t.Errorf("Walk through %s .. wanted EINVAL, got %v", name, err) + } + for _, invalidName := range allInvalidNames(name) { + if _, _, err := root.Walk([]string{invalidName}); err != syscall.EINVAL { + t.Errorf("Walk through %s wanted EINVAL, got %v", invalidName, err) + } + } + wantErr := syscall.EINVAL + if name == "directory" { + // We can attempt a walk through a directory. However, + // we should never see a file named "other", so we + // expect this to return ENOENT. + wantErr = syscall.ENOENT + } + if _, _, err := root.Walk([]string{name, "other"}); err != wantErr { + t.Errorf("Walk through %s/other wanted %v, got %v", name, wantErr, err) + } + + // Do a successful walk. + _, f, err := root.Walk([]string{name}) + if err != nil { + t.Errorf("Walk to %s wanted nil, got %v", name, err) + } + defer f.Close() + local := h.Pop(f) + + // Check that the file matches. + _, localMask, localAttr, localErr := local.GetAttr(p9.AttrMaskAll()) + if _, mask, attr, err := f.GetAttr(p9.AttrMaskAll()); mask != localMask || attr != localAttr || err != localErr { + t.Errorf("GetAttr got (%v, %v, %v), wanted (%v, %v, %v)", + mask, attr, err, localMask, localAttr, localErr) + } + + // Ensure we can't walk backwards. + if _, _, err := f.Walk([]string{"."}); err != syscall.EINVAL { + t.Errorf("Walk through %s/. wanted EINVAL, got %v", name, err) + } + if _, _, err := f.Walk([]string{".."}); err != syscall.EINVAL { + t.Errorf("Walk through %s/.. wanted EINVAL, got %v", name, err) + } + } +} + +// fileGenerator is a function to generate files via walk or create. +// +// Examples are: +// - walkHelper +// - walkAndOpenHelper +// - createHelper +type fileGenerator func(*Harness, string, p9.File) (*Mock, *Mock, p9.File) + +// walkHelper walks to the given file. +// +// The backends of the parent and walked file are returned, as well as the +// walked client file. +func walkHelper(h *Harness, name string, dir p9.File) (parentBackend *Mock, walkedBackend *Mock, walked p9.File) { + _, parent, err := dir.Walk(nil) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer parent.Close() + parentBackend = h.Pop(parent) + + _, walked, err = parent.Walk([]string{name}) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + walkedBackend = h.Pop(walked) + + return parentBackend, walkedBackend, walked +} + +// walkAndOpenHelper additionally opens the walked file, if possible. +func walkAndOpenHelper(h *Harness, name string, dir p9.File) (*Mock, *Mock, p9.File) { + parentBackend, walkedBackend, walked := walkHelper(h, name, dir) + if p9.CanOpen(walkedBackend.Attr.Mode) { + // Open for all file types that we can. We stick to a read-only + // open here because directories may not be opened otherwise. + walkedBackend.EXPECT().Open(p9.ReadOnly).Times(1) + if _, _, _, err := walked.Open(p9.ReadOnly); err != nil { + h.t.Errorf("got open err %v, want nil", err) + } + } else { + // ... or assert an error for others. + if _, _, _, err := walked.Open(p9.ReadOnly); err != syscall.EINVAL { + h.t.Errorf("got open err %v, want EINVAL", err) + } + } + return parentBackend, walkedBackend, walked +} + +// createHelper creates the given file and returns the parent directory, +// created file and client file, which must be closed when done. +func createHelper(h *Harness, name string, dir p9.File) (*Mock, *Mock, p9.File) { + // Clone the directory first, since Create replaces the existing file. + // We change the type after calling create. + _, dirThenFile, err := dir.Walk(nil) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + + // Create a new server-side file. On the server-side, the a new file is + // returned from a create call. The client will reuse the same file, + // but we still expect the normal chain of closes. This complicates + // things a bit because the "parent" will always chain to the cloned + // dir above. + dirBackend := h.Pop(dirThenFile) // New backend directory. + newFile := h.NewFile()(dirBackend) // New file with backend parent. + dirBackend.EXPECT().Create(name, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, newFile, newFile.QID, uint32(0), nil) + + // Create via the client. + _, dirThenFile, _, _, err = dirThenFile.Create(name, p9.ReadOnly, 0, 0, 0) if err != nil { - t.Fatalf("could not create temporary file: %v", err) + h.t.Fatalf("got create err %v, want nil", err) + } + + // Ensure subsequent walks succeed. + dirBackend.AddChild(name, h.NewFile()) + return dirBackend, newFile, dirThenFile +} + +// deprecatedRemover allows us to access the deprecated Remove operation within +// the p9.File client object. +type deprecatedRemover interface { + Remove() error +} + +// checkDeleted asserts that relevant methods fail for an unlinked file. +// +// This function will close the file at the end. +func checkDeleted(h *Harness, file p9.File) { + defer file.Close() // See doc. + + if _, _, _, err := file.Open(p9.ReadOnly); err != syscall.EINVAL { + h.t.Errorf("open while deleted, got %v, want EINVAL", err) + } + if _, _, _, _, err := file.Create("created", p9.ReadOnly, 0, 0, 0); err != syscall.EINVAL { + h.t.Errorf("create while deleted, got %v, want EINVAL", err) + } + if _, err := file.Symlink("old", "new", 0, 0); err != syscall.EINVAL { + h.t.Errorf("symlink while deleted, got %v, want EINVAL", err) + } + // N.B. This link is technically invalid, but if a call to link is + // actually made in the backend then the mock will panic. + if err := file.Link(file, "new"); err != syscall.EINVAL { + h.t.Errorf("link while deleted, got %v, want EINVAL", err) + } + if err := file.RenameAt("src", file, "dst"); err != syscall.EINVAL { + h.t.Errorf("renameAt while deleted, got %v, want EINVAL", err) + } + if err := file.UnlinkAt("file", 0); err != syscall.EINVAL { + h.t.Errorf("unlinkAt while deleted, got %v, want EINVAL", err) + } + if err := file.Rename(file, "dst"); err != syscall.EINVAL { + h.t.Errorf("rename while deleted, got %v, want EINVAL", err) + } + if _, err := file.Readlink(); err != syscall.EINVAL { + h.t.Errorf("readlink while deleted, got %v, want EINVAL", err) + } + if _, err := file.Mkdir("dir", p9.ModeDirectory, 0, 0); err != syscall.EINVAL { + h.t.Errorf("mkdir while deleted, got %v, want EINVAL", err) + } + if _, err := file.Mknod("dir", p9.ModeDirectory, 0, 0, 0, 0); err != syscall.EINVAL { + h.t.Errorf("mknod while deleted, got %v, want EINVAL", err) + } + if _, err := file.Readdir(0, 1); err != syscall.EINVAL { + h.t.Errorf("readdir while deleted, got %v, want EINVAL", err) + } + if _, err := file.Connect(p9.ConnectFlags(0)); err != syscall.EINVAL { + h.t.Errorf("connect while deleted, got %v, want EINVAL", err) + } + + // The remove method is technically deprecated, but we want to ensure + // that it still checks for deleted appropriately. We must first clone + // the file because remove is equivalent to close. + _, newFile, err := file.Walk(nil) + if err == syscall.EBUSY { + // We can't walk from here because this reference is open + // aleady. Okay, we will also have unopened cases through + // TestUnlink, just skip the remove operation for now. + return + } else if err != nil { + h.t.Fatalf("clone failed, got %v, want nil", err) + } + if err := newFile.(deprecatedRemover).Remove(); err != syscall.EINVAL { + h.t.Errorf("remove while deleted, got %v, want EINVAL", err) + } +} + +// deleter is a function to remove a file. +type deleter func(parent p9.File, name string) error + +// unlinkAt is a deleter. +func unlinkAt(parent p9.File, name string) error { + // Call unlink. Note that a filesystem may normally impose additional + // constaints on unlinkat success, such as ensuring that a directory is + // empty, requiring AT_REMOVEDIR in flags to remove a directory, etc. + // None of that is required internally (entire trees can be marked + // deleted when this operation succeeds), so the mock will succeed. + return parent.UnlinkAt(name, 0) +} + +// remove is a deleter. +func remove(parent p9.File, name string) error { + // See notes above re: remove. + _, newFile, err := parent.Walk([]string{name}) + if err != nil { + // Should not be expected. + return err + } + + // Do the actual remove. + if err := newFile.(deprecatedRemover).Remove(); err != nil { + return err + } + + // Ensure that the remove closed the file. + if err := newFile.(deprecatedRemover).Remove(); err != syscall.EBADF { + return syscall.EBADF // Propagate this code. + } + + return nil +} + +// unlinkHelper unlinks the noted path, and ensures that all relevant +// operations on that path, acquired from multiple paths, start failing. +func unlinkHelper(h *Harness, root p9.File, targetNames []string, targetGen fileGenerator, deleteFn deleter) { + // name is the file to be unlinked. + name := targetNames[len(targetNames)-1] + + // Walk to the directory containing the target. + _, parent, err := root.Walk(targetNames[:len(targetNames)-1]) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer parent.Close() + parentBackend := h.Pop(parent) + + // Walk to or generate the target file. + _, _, target := targetGen(h, name, parent) + defer checkDeleted(h, target) + + // Walk to a second reference. + _, second, err := parent.Walk([]string{name}) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer checkDeleted(h, second) + + // Walk to a third reference, from the start. + _, third, err := root.Walk(targetNames) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer checkDeleted(h, third) + + // This will be translated in the backend to an unlinkat. + parentBackend.EXPECT().UnlinkAt(name, uint32(0)).Return(nil) + + // Actually perform the deletion. + if err := deleteFn(parent, name); err != nil { + h.t.Fatalf("got delete err %v, want nil", err) + } +} + +func unlinkTest(t *testing.T, targetNames []string, targetGen fileGenerator) { + t.Run(fmt.Sprintf("unlinkAt(%s)", strings.Join(targetNames, "/")), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + unlinkHelper(h, root, targetNames, targetGen, unlinkAt) + }) + t.Run(fmt.Sprintf("remove(%s)", strings.Join(targetNames, "/")), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + unlinkHelper(h, root, targetNames, targetGen, remove) + }) +} + +func TestUnlink(t *testing.T) { + // Unlink all files. + for name := range newTypeMap(nil) { + unlinkTest(t, []string{name}, walkHelper) + unlinkTest(t, []string{name}, walkAndOpenHelper) + unlinkTest(t, []string{"one", name}, walkHelper) + unlinkTest(t, []string{"one", name}, walkAndOpenHelper) + unlinkTest(t, []string{"one", "two", name}, walkHelper) + unlinkTest(t, []string{"one", "two", name}, walkAndOpenHelper) + } + + // Unlink a directory. + unlinkTest(t, []string{"one"}, walkHelper) + unlinkTest(t, []string{"one"}, walkAndOpenHelper) + unlinkTest(t, []string{"one", "two"}, walkHelper) + unlinkTest(t, []string{"one", "two"}, walkAndOpenHelper) + + // Unlink created files. + unlinkTest(t, []string{"created"}, createHelper) + unlinkTest(t, []string{"one", "created"}, createHelper) + unlinkTest(t, []string{"one", "two", "created"}, createHelper) +} + +func TestUnlinkAtInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + for name := range newTypeMap(nil) { + for _, invalidName := range allInvalidNames(name) { + if err := root.UnlinkAt(invalidName, 0); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + } + } +} + +// expectRenamed asserts an ordered sequence of rename calls, based on all the +// elements in elements being the source, and the first element therein +// changing to dstName, parented at dstParent. +func expectRenamed(file *Mock, elements []string, dstParent *Mock, dstName string) *gomock.Call { + if len(elements) > 0 { + // Recurse to the parent, if necessary. + call := expectRenamed(file.parent, elements[:len(elements)-1], dstParent, dstName) + + // Recursive case: this element is unchanged, but should have + // it's hook called after the parent. + return file.EXPECT().Renamed(file.parent, elements[len(elements)-1]).Do(func(p p9.File, _ string) { + file.parent = p.(*Mock) + }).After(call) + } + + // Base case: this is the changed element. + return file.EXPECT().Renamed(dstParent, dstName).Do(func(p p9.File, name string) { + file.parent = p.(*Mock) + }) +} + +// renamer is a rename function. +type renamer func(h *Harness, srcParent, dstParent p9.File, origName, newName string, selfRename bool) error + +// renameAt is a renamer. +func renameAt(_ *Harness, srcParent, dstParent p9.File, srcName, dstName string, selfRename bool) error { + return srcParent.RenameAt(srcName, dstParent, dstName) +} + +// rename is a renamer. +func rename(h *Harness, srcParent, dstParent p9.File, srcName, dstName string, selfRename bool) error { + _, f, err := srcParent.Walk([]string{srcName}) + if err != nil { + return err + } + defer f.Close() + if !selfRename { + backend := h.Pop(f) + backend.EXPECT().Renamed(gomock.Any(), dstName).Do(func(p p9.File, name string) { + backend.parent = p.(*Mock) // Required for close ordering. + }) + } + return f.Rename(dstParent, dstName) +} + +// renameHelper executes a rename, and asserts that all relevant elements +// receive expected notifications. If overwriting a file, this includes +// ensuring that the target has been appropriately marked as unlinked. +func renameHelper(h *Harness, root p9.File, srcNames []string, dstNames []string, target fileGenerator, renameFn renamer) { + // Walk to the directory containing the target. + srcQID, targetParent, err := root.Walk(srcNames[:len(srcNames)-1]) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer targetParent.Close() + targetParentBackend := h.Pop(targetParent) + + // Walk to or generate the target file. + _, targetBackend, src := target(h, srcNames[len(srcNames)-1], targetParent) + defer src.Close() + + // Walk to a second reference. + _, second, err := targetParent.Walk([]string{srcNames[len(srcNames)-1]}) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer second.Close() + secondBackend := h.Pop(second) + + // Walk to a third reference, from the start. + _, third, err := root.Walk(srcNames) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer third.Close() + thirdBackend := h.Pop(third) + + // Find the common suffix to identify the rename parent. + var ( + renameDestPath []string + renameSrcPath []string + selfRename bool + ) + for i := 1; i <= len(srcNames) && i <= len(dstNames); i++ { + if srcNames[len(srcNames)-i] != dstNames[len(dstNames)-i] { + // Take the full prefix of dstNames up until this + // point, including the first mismatched name. The + // first mismatch must be the renamed entry. + renameDestPath = dstNames[:len(dstNames)-i+1] + renameSrcPath = srcNames[:len(srcNames)-i+1] + + // Does the renameDestPath fully contain the + // renameSrcPath here? If yes, then this is a mismatch. + // We can't rename the src to some subpath of itself. + if len(renameDestPath) > len(renameSrcPath) && + reflect.DeepEqual(renameDestPath[:len(renameSrcPath)], renameSrcPath) { + renameDestPath = nil + renameSrcPath = nil + continue + } + break + } + } + if len(renameSrcPath) == 0 || len(renameDestPath) == 0 { + // This must be a rename to self, or a tricky look-alike. This + // happens iff we fail to find a suitable divergence in the two + // paths. It's a true self move if the path length is the same. + renameDestPath = dstNames + renameSrcPath = srcNames + selfRename = len(srcNames) == len(dstNames) + } + + // Walk to the source parent. + _, srcParent, err := root.Walk(renameSrcPath[:len(renameSrcPath)-1]) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer srcParent.Close() + srcParentBackend := h.Pop(srcParent) + + // Walk to the destination parent. + _, dstParent, err := root.Walk(renameDestPath[:len(renameDestPath)-1]) + if err != nil { + h.t.Fatalf("got walk err %v, want nil", err) + } + defer dstParent.Close() + dstParentBackend := h.Pop(dstParent) + + // expectedErr is the result of the rename operation. + var expectedErr error + + // Walk to the target file, if one exists. + dstQID, dst, err := root.Walk(renameDestPath) + if err == nil { + if !selfRename && srcQID[0].Type == dstQID[0].Type { + // If there is a destination file, and is it of the + // same type as the source file, then we expect the + // rename to succeed. We expect the destination file to + // be deleted, so we run a deletion test on it in this + // case. + defer checkDeleted(h, dst) + } else { + if !selfRename { + // If the type is different than the + // destination, then we expect the rename to + // fail. We expect ensure that this is + // returned. + expectedErr = syscall.EINVAL + } else { + // This is the file being renamed to itself. + // This is technically allowed and a no-op, but + // all the triggers will fire. + } + dst.Close() + } + } + dstName := renameDestPath[len(renameDestPath)-1] // Renamed element. + srcName := renameSrcPath[len(renameSrcPath)-1] // Renamed element. + if expectedErr == nil && !selfRename { + // Expect all to be renamed appropriately. Note that if this is + // a final file being renamed, then we expect the file to be + // called with the new parent. If not, then we expect the + // rename hook to be called, but the parent will remain + // unchanged. + elements := srcNames[len(renameSrcPath):] + expectRenamed(targetBackend, elements, dstParentBackend, dstName) + expectRenamed(secondBackend, elements, dstParentBackend, dstName) + expectRenamed(thirdBackend, elements, dstParentBackend, dstName) + + // The target parent has also been opened, and may be moved + // directly or indirectly. + if len(elements) > 1 { + expectRenamed(targetParentBackend, elements[:len(elements)-1], dstParentBackend, dstName) + } + } + + // Expect the rename if it's not the same file. Note that like unlink, + // renames are always translated to the at variant in the backend. + if !selfRename { + srcParentBackend.EXPECT().RenameAt(srcName, dstParentBackend, dstName).Return(expectedErr) + } + + // Perform the actual rename; everything has been lined up. + if err := renameFn(h, srcParent, dstParent, srcName, dstName, selfRename); err != expectedErr { + h.t.Fatalf("got rename err %v, want %v", err, expectedErr) + } +} + +func renameTest(t *testing.T, srcNames []string, dstNames []string, target fileGenerator) { + t.Run(fmt.Sprintf("renameAt(%s->%s)", strings.Join(srcNames, "/"), strings.Join(dstNames, "/")), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + renameHelper(h, root, srcNames, dstNames, target, renameAt) + }) + t.Run(fmt.Sprintf("rename(%s->%s)", strings.Join(srcNames, "/"), strings.Join(dstNames, "/")), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + renameHelper(h, root, srcNames, dstNames, target, rename) + }) +} + +func TestRename(t *testing.T) { + // In-directory rename, simple case. + for name := range newTypeMap(nil) { + // Within the root. + renameTest(t, []string{name}, []string{"renamed"}, walkHelper) + renameTest(t, []string{name}, []string{"renamed"}, walkAndOpenHelper) + + // Within a subdirectory. + renameTest(t, []string{"one", name}, []string{"one", "renamed"}, walkHelper) + renameTest(t, []string{"one", name}, []string{"one", "renamed"}, walkAndOpenHelper) + } + + // ... with created files. + renameTest(t, []string{"created"}, []string{"renamed"}, createHelper) + renameTest(t, []string{"one", "created"}, []string{"one", "renamed"}, createHelper) + + // Across directories. + for name := range newTypeMap(nil) { + // Down one level. + renameTest(t, []string{"one", name}, []string{"one", "two", "renamed"}, walkHelper) + renameTest(t, []string{"one", name}, []string{"one", "two", "renamed"}, walkAndOpenHelper) + + // Up one level. + renameTest(t, []string{"one", "two", name}, []string{"one", "renamed"}, walkHelper) + renameTest(t, []string{"one", "two", name}, []string{"one", "renamed"}, walkAndOpenHelper) + + // Across at the same level. + renameTest(t, []string{"one", name}, []string{"three", "renamed"}, walkHelper) + renameTest(t, []string{"one", name}, []string{"three", "renamed"}, walkAndOpenHelper) + } + + // ... with created files. + renameTest(t, []string{"one", "created"}, []string{"one", "two", "renamed"}, createHelper) + renameTest(t, []string{"one", "two", "created"}, []string{"one", "renamed"}, createHelper) + renameTest(t, []string{"one", "created"}, []string{"three", "renamed"}, createHelper) + + // Renaming parents. + for name := range newTypeMap(nil) { + // Rename a parent. + renameTest(t, []string{"one", name}, []string{"renamed", name}, walkHelper) + renameTest(t, []string{"one", name}, []string{"renamed", name}, walkAndOpenHelper) + + // Rename a super parent. + renameTest(t, []string{"one", "two", name}, []string{"renamed", name}, walkHelper) + renameTest(t, []string{"one", "two", name}, []string{"renamed", name}, walkAndOpenHelper) + } + + // ... with created files. + renameTest(t, []string{"one", "created"}, []string{"renamed", "created"}, createHelper) + renameTest(t, []string{"one", "two", "created"}, []string{"renamed", "created"}, createHelper) + + // Over existing files, including itself. + for name := range newTypeMap(nil) { + for other := range newTypeMap(nil) { + // Overwrite the noted file (may be itself). + renameTest(t, []string{"one", name}, []string{"one", other}, walkHelper) + renameTest(t, []string{"one", name}, []string{"one", other}, walkAndOpenHelper) + + // Overwrite other files in another directory. + renameTest(t, []string{"one", name}, []string{"one", "two", other}, walkHelper) + renameTest(t, []string{"one", name}, []string{"one", "two", other}, walkAndOpenHelper) + } + + // Overwrite by moving the parent. + renameTest(t, []string{"three", name}, []string{"one", name}, walkHelper) + renameTest(t, []string{"three", name}, []string{"one", name}, walkAndOpenHelper) + + // Create over the types. + renameTest(t, []string{"one", "created"}, []string{"one", name}, createHelper) + renameTest(t, []string{"one", "created"}, []string{"one", "two", name}, createHelper) + renameTest(t, []string{"three", "created"}, []string{"one", name}, createHelper) + } +} + +func TestRenameInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + for name := range newTypeMap(nil) { + for _, invalidName := range allInvalidNames(name) { + if err := root.Rename(root, invalidName); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + } + } +} + +func TestRenameAtInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + for name := range newTypeMap(nil) { + for _, invalidName := range allInvalidNames(name) { + if err := root.RenameAt(invalidName, root, "okay"); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + if err := root.RenameAt("okay", root, invalidName); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + } + } +} + +func TestReadlink(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + // Walk to the file normally. + _, f, err := root.Walk([]string{name}) + if err != nil { + t.Fatalf("walk failed: got %v, wanted nil", err) + } + defer f.Close() + backend := h.Pop(f) + + const symlinkTarget = "symlink-target" + + if backend.Attr.Mode.IsSymlink() { + // This should only go through on symlinks. + backend.EXPECT().Readlink().Return(symlinkTarget, nil) + } + + // Attempt a Readlink operation. + target, err := f.Readlink() + if err != nil && err != syscall.EINVAL { + t.Errorf("readlink got %v, wanted EINVAL", err) + } else if err == nil && target != symlinkTarget { + t.Errorf("readlink got %v, wanted %v", target, symlinkTarget) + } + }) + } +} + +// fdTest is a wrapper around operations that may send file descriptors. This +// asserts that the file descriptors are working as intended. +func fdTest(t *testing.T, sendFn func(*fd.FD) *fd.FD) { + // Create a pipe that we can read from. + r, w, err := os.Pipe() + if err != nil { + t.Fatalf("unable to create pipe: %v", err) + } + defer r.Close() + defer w.Close() + + // Attempt to send the write end. + wFD, err := fd.NewFromFile(w) + if err != nil { + t.Fatalf("unable to convert file: %v", err) + } + defer wFD.Close() // This is a copy. + + // Send wFD and receive newFD. + newFD := sendFn(wFD) + defer newFD.Close() + + // Attempt to write. + const message = "hello" + if _, err := newFD.Write([]byte(message)); err != nil { + t.Fatalf("write got %v, wanted nil", err) + } + + // Should see the message on our end. + buffer := []byte(message) + if _, err := io.ReadFull(r, buffer); err != nil { + t.Fatalf("read got %v, wanted nil", err) + } + if string(buffer) != message { + t.Errorf("got message %v, wanted %v", string(buffer), message) + } +} + +func TestConnect(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + // Walk to the file normally. + _, backend, f := walkHelper(h, name, root) + defer f.Close() + + // Catch all the non-socket cases. + if !backend.Attr.Mode.IsSocket() { + // This has been set up to fail if Connect is called. + if _, err := f.Connect(p9.ConnectFlags(0)); err != syscall.EINVAL { + t.Errorf("connect got %v, wanted EINVAL", err) + } + return + } + + // Ensure the fd exchange works. + fdTest(t, func(send *fd.FD) *fd.FD { + backend.EXPECT().Connect(p9.ConnectFlags(0)).Return(send, nil) + recv, err := backend.Connect(p9.ConnectFlags(0)) + if err != nil { + t.Fatalf("connect got %v, wanted nil", err) + } + return recv + }) + }) + } +} + +func TestReaddir(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + // Walk to the file normally. + _, backend, f := walkHelper(h, name, root) + defer f.Close() + + // Catch all the non-directory cases. + if !backend.Attr.Mode.IsDir() { + // This has also been set up to fail if Readdir is called. + if _, err := f.Readdir(0, 1); err != syscall.EINVAL { + t.Errorf("readdir got %v, wanted EINVAL", err) + } + return + } + + // Ensure that readdir works for directories. + if _, err := f.Readdir(0, 1); err != syscall.EINVAL { + t.Errorf("readdir got %v, wanted EINVAL", err) + } + if _, _, _, err := f.Open(p9.ReadWrite); err != syscall.EINVAL { + t.Errorf("readdir got %v, wanted EINVAL", err) + } + if _, _, _, err := f.Open(p9.WriteOnly); err != syscall.EINVAL { + t.Errorf("readdir got %v, wanted EINVAL", err) + } + backend.EXPECT().Open(p9.ReadOnly).Times(1) + if _, _, _, err := f.Open(p9.ReadOnly); err != nil { + t.Errorf("readdir got %v, wanted nil", err) + } + backend.EXPECT().Readdir(uint64(0), uint32(1)).Times(1) + if _, err := f.Readdir(0, 1); err != nil { + t.Errorf("readdir got %v, wanted nil", err) + } + }) + } +} + +func TestOpen(t *testing.T) { + type openTest struct { + name string + mode p9.OpenFlags + err error + match func(p9.FileMode) bool + } + + cases := []openTest{ + { + name: "invalid", + mode: ^p9.OpenFlagsModeMask, + err: syscall.EINVAL, + match: func(p9.FileMode) bool { return true }, + }, + { + name: "not-openable-read-only", + mode: p9.ReadOnly, + err: syscall.EINVAL, + match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) }, + }, + { + name: "not-openable-write-only", + mode: p9.WriteOnly, + err: syscall.EINVAL, + match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) }, + }, + { + name: "not-openable-read-write", + mode: p9.ReadWrite, + err: syscall.EINVAL, + match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) }, + }, + { + name: "directory-read-only", + mode: p9.ReadOnly, + err: nil, + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + }, + { + name: "directory-read-write", + mode: p9.ReadWrite, + err: syscall.EINVAL, + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + }, + { + name: "directory-write-only", + mode: p9.WriteOnly, + err: syscall.EINVAL, + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + }, + { + name: "read-only", + mode: p9.ReadOnly, + err: nil, + match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) }, + }, + { + name: "write-only", + mode: p9.WriteOnly, + err: nil, + match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() }, + }, + { + name: "read-write", + mode: p9.ReadWrite, + err: nil, + match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() }, + }, + } + + // Open(mode OpenFlags) (*fd.FD, QID, uint32, error) + // - only works on Regular, NamedPipe, BLockDevice, CharacterDevice + // - returning a file works as expected + for name := range newTypeMap(nil) { + for _, tc := range cases { + t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + // Walk to the file normally. + _, backend, f := walkHelper(h, name, root) + defer f.Close() + + // Does this match the case? + if !tc.match(backend.Attr.Mode) { + t.SkipNow() + } + + // Ensure open-required operations fail. + if _, err := f.ReadAt([]byte("hello"), 0); err != syscall.EINVAL { + t.Errorf("readAt got %v, wanted EINVAL", err) + } + if _, err := f.WriteAt(make([]byte, 6), 0); err != syscall.EINVAL { + t.Errorf("writeAt got %v, wanted EINVAL", err) + } + if err := f.FSync(); err != syscall.EINVAL { + t.Errorf("fsync got %v, wanted EINVAL", err) + } + if _, err := f.Readdir(0, 1); err != syscall.EINVAL { + t.Errorf("readdir got %v, wanted EINVAL", err) + } + + // Attempt the given open. + if tc.err != nil { + // We expect an error, just test and return. + if _, _, _, err := f.Open(tc.mode); err != tc.err { + t.Fatalf("open with mode %v got %v, want %v", tc.mode, err, tc.err) + } + return + } + + // Run an FD test, since we expect success. + fdTest(t, func(send *fd.FD) *fd.FD { + backend.EXPECT().Open(tc.mode).Return(send, p9.QID{}, uint32(0), nil).Times(1) + recv, _, _, err := f.Open(tc.mode) + if err != tc.err { + t.Fatalf("open with mode %v got %v, want %v", tc.mode, err, tc.err) + } + return recv + }) + + // If the open was successful, attempt another one. + if _, _, _, err := f.Open(tc.mode); err != syscall.EINVAL { + t.Errorf("second open with mode %v got %v, want EINVAL", tc.mode, err) + } + + // Ensure that all illegal operations fail. + if _, _, err := f.Walk(nil); err != syscall.EINVAL && err != syscall.EBUSY { + t.Errorf("walk got %v, wanted EINVAL or EBUSY", err) + } + if _, _, _, _, err := f.WalkGetAttr(nil); err != syscall.EINVAL && err != syscall.EBUSY { + t.Errorf("walkgetattr got %v, wanted EINVAL or EBUSY", err) + } + }) + } + } +} + +func TestClose(t *testing.T) { + type closeTest struct { + name string + closeFn func(backend *Mock, f p9.File) + } + + cases := []closeTest{ + { + name: "close", + closeFn: func(_ *Mock, f p9.File) { + f.Close() + }, + }, + { + name: "remove", + closeFn: func(backend *Mock, f p9.File) { + // Allow the rename call in the parent, automatically translated. + backend.parent.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Times(1) + f.(deprecatedRemover).Remove() + }, + }, } - os.Remove(osFile.Name()) - hfi, err := osFile.Stat() - if err != nil { - osFile.Close() - t.Fatalf("stat failed: %v", err) + for name := range newTypeMap(nil) { + for _, tc := range cases { + t.Run(fmt.Sprintf("%s(%s)", tc.name, name), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + // Walk to the file normally. + _, backend, f := walkHelper(h, name, root) + + // Close via the prescribed method. + tc.closeFn(backend, f) + + // Everything should fail with EBADF. + if _, _, err := f.Walk(nil); err != syscall.EBADF { + t.Errorf("walk got %v, wanted EBADF", err) + } + if _, err := f.StatFS(); err != syscall.EBADF { + t.Errorf("statfs got %v, wanted EBADF", err) + } + if _, _, _, err := f.GetAttr(p9.AttrMaskAll()); err != syscall.EBADF { + t.Errorf("getattr got %v, wanted EBADF", err) + } + if err := f.SetAttr(p9.SetAttrMask{}, p9.SetAttr{}); err != syscall.EBADF { + t.Errorf("setattrk got %v, wanted EBADF", err) + } + if err := f.Rename(root, "new-name"); err != syscall.EBADF { + t.Errorf("rename got %v, wanted EBADF", err) + } + if err := f.Close(); err != syscall.EBADF { + t.Errorf("close got %v, wanted EBADF", err) + } + if _, _, _, err := f.Open(p9.ReadOnly); err != syscall.EBADF { + t.Errorf("open got %v, wanted EBADF", err) + } + if _, err := f.ReadAt([]byte("hello"), 0); err != syscall.EBADF { + t.Errorf("readAt got %v, wanted EBADF", err) + } + if _, err := f.WriteAt(make([]byte, 6), 0); err != syscall.EBADF { + t.Errorf("writeAt got %v, wanted EBADF", err) + } + if err := f.FSync(); err != syscall.EBADF { + t.Errorf("fsync got %v, wanted EBADF", err) + } + if _, _, _, _, err := f.Create("new-file", p9.ReadWrite, 0, 0, 0); err != syscall.EBADF { + t.Errorf("create got %v, wanted EBADF", err) + } + if _, err := f.Mkdir("new-directory", 0, 0, 0); err != syscall.EBADF { + t.Errorf("mkdir got %v, wanted EBADF", err) + } + if _, err := f.Symlink("old-name", "new-name", 0, 0); err != syscall.EBADF { + t.Errorf("symlink got %v, wanted EBADF", err) + } + if err := f.Link(root, "new-name"); err != syscall.EBADF { + t.Errorf("link got %v, wanted EBADF", err) + } + if _, err := f.Mknod("new-block-device", 0, 0, 0, 0, 0); err != syscall.EBADF { + t.Errorf("mknod got %v, wanted EBADF", err) + } + if err := f.RenameAt("old-name", root, "new-name"); err != syscall.EBADF { + t.Errorf("renameAt got %v, wanted EBADF", err) + } + if err := f.UnlinkAt("name", 0); err != syscall.EBADF { + t.Errorf("unlinkAt got %v, wanted EBADF", err) + } + if _, err := f.Readdir(0, 1); err != syscall.EBADF { + t.Errorf("readdir got %v, wanted EBADF", err) + } + if _, err := f.Readlink(); err != syscall.EBADF { + t.Errorf("readlink got %v, wanted EBADF", err) + } + if err := f.Flush(); err != syscall.EBADF { + t.Errorf("flush got %v, wanted EBADF", err) + } + if _, _, _, _, err := f.WalkGetAttr(nil); err != syscall.EBADF { + t.Errorf("walkgetattr got %v, wanted EBADF", err) + } + if _, err := f.Connect(p9.ConnectFlags(0)); err != syscall.EBADF { + t.Errorf("connect got %v, wanted EBADF", err) + } + }) + } + } +} + +// onlyWorksOnOpenThings is a helper test method for operations that should +// only work on files that have been explicitly opened. +func onlyWorksOnOpenThings(h *Harness, t *testing.T, name string, root p9.File, mode p9.OpenFlags, expectedErr error, fn func(backend *Mock, f p9.File, shouldSucceed bool) error) { + // Walk to the file normally. + _, backend, f := walkHelper(h, name, root) + defer f.Close() + + // Does it work before opening? + if err := fn(backend, f, false); err != syscall.EINVAL { + t.Errorf("operation got %v, wanted EINVAL", err) } - osFileStat := hfi.Sys().(*syscall.Stat_t) - f, err := fd.NewFromFile(osFile) - // osFile should always be closed. - osFile.Close() - if err != nil { - t.Fatalf("unable to create file: %v", err) + // Is this openable? + if !p9.CanOpen(backend.Attr.Mode) { + return // Nothing to do. + } + + // If this is a directory, we can't handle writing. + if backend.Attr.Mode.IsDir() && (mode == p9.ReadWrite || mode == p9.WriteOnly) { + return // Skip. + } + + // Open the file. + backend.EXPECT().Open(mode) + if _, _, _, err := f.Open(mode); err != nil { + t.Fatalf("open got %v, wanted nil", err) + } + + // Attempt the operation. + if err := fn(backend, f, expectedErr == nil); err != expectedErr { + t.Fatalf("operation got %v, wanted %v", err, expectedErr) + } +} + +func TestRead(t *testing.T) { + type readTest struct { + name string + mode p9.OpenFlags + err error + } + + cases := []readTest{ + { + name: "read-only", + mode: p9.ReadOnly, + err: nil, + }, + { + name: "read-write", + mode: p9.ReadWrite, + err: nil, + }, + { + name: "write-only", + mode: p9.WriteOnly, + err: syscall.EPERM, + }, + } + + for name := range newTypeMap(nil) { + for _, tc := range cases { + t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + const message = "hello" + + onlyWorksOnOpenThings(h, t, name, root, tc.mode, tc.err, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if !shouldSucceed { + _, err := f.ReadAt([]byte(message), 0) + return err + } + + // Prepare for the call to readAt in the backend. + backend.EXPECT().ReadAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) { + copy(p, message) + }).Return(len(message), nil) + + // Make the client call. + p := make([]byte, 2*len(message)) // Double size. + n, err := f.ReadAt(p, 0) + + // Sanity check result. + if err != nil { + return err + } + if n != len(message) { + t.Fatalf("message length incorrect, got %d, want %d", n, len(message)) + } + if !bytes.Equal(p[:n], []byte(message)) { + t.Fatalf("message incorrect, got %v, want %v", p, []byte(message)) + } + return nil // Success. + }) + }) + } + } +} + +func TestWrite(t *testing.T) { + type writeTest struct { + name string + mode p9.OpenFlags + err error } - // Craft attacher to attach to the mocked file which will return our - // temporary file. - fileMock := &FileMock{ - OpenMock: OpenMock{File: f}, - GetAttrMock: GetAttrMock{ - // The mode must be valid always. - Valid: p9.AttrMask{Mode: true}, + cases := []writeTest{ + { + name: "read-only", + mode: p9.ReadOnly, + err: syscall.EPERM, + }, + { + name: "read-write", + mode: p9.ReadWrite, + err: nil, + }, + { + name: "write-only", + mode: p9.WriteOnly, + err: nil, }, } - attacher := &AttachMock{ - File: fileMock, + + for name := range newTypeMap(nil) { + for _, tc := range cases { + t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + const message = "hello" + + onlyWorksOnOpenThings(h, t, name, root, tc.mode, tc.err, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if !shouldSucceed { + _, err := f.WriteAt([]byte(message), 0) + return err + } + + // Prepare for the call to readAt in the backend. + var output []byte // Saved by Do below. + backend.EXPECT().WriteAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) { + output = p + }).Return(len(message), nil) + + // Make the client call. + n, err := f.WriteAt([]byte(message), 0) + + // Sanity check result. + if err != nil { + return err + } + if n != len(message) { + t.Fatalf("message length incorrect, got %d, want %d", n, len(message)) + } + if !bytes.Equal(output, []byte(message)) { + t.Fatalf("message incorrect, got %v, want %v", output, []byte(message)) + } + return nil // Success. + }) + }) + } } +} - // Make socket pair. - serverSocket, clientSocket, err := unet.SocketPair(false) - if err != nil { - t.Fatalf("socketpair got err %v wanted nil", err) +func TestFSync(t *testing.T) { + for name := range newTypeMap(nil) { + for _, mode := range []p9.OpenFlags{p9.ReadOnly, p9.WriteOnly, p9.ReadWrite} { + t.Run(fmt.Sprintf("%s-%s", mode, name), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + onlyWorksOnOpenThings(h, t, name, root, mode, nil, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if shouldSucceed { + backend.EXPECT().FSync().Times(1) + } + return f.FSync() + }) + }) + } } - defer clientSocket.Close() - server := p9.NewServer(attacher) - go server.Handle(serverSocket) - client, err := p9.NewClient(clientSocket, 1024*1024 /* 1M message size */, p9.HighestVersionString()) - if err != nil { - t.Fatalf("new client got %v, expected nil", err) +} + +func TestFlush(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + _, backend, f := walkHelper(h, name, root) + defer f.Close() + + backend.EXPECT().Flush() + f.Flush() + }) } +} - // Attach to the mocked file. - cFile, err := client.Attach("") - if err != nil { - t.Fatalf("attach failed: %v", err) +// onlyWorksOnDirectories is a helper test method for operations that should +// only work on unopened directories, such as create, mkdir and symlink. +func onlyWorksOnDirectories(h *Harness, t *testing.T, name string, root p9.File, fn func(backend *Mock, f p9.File, shouldSucceed bool) error) { + // Walk to the file normally. + _, backend, f := walkHelper(h, name, root) + defer f.Close() + + // Only directories support mknod. + if !backend.Attr.Mode.IsDir() { + if err := fn(backend, f, false); err != syscall.EINVAL { + t.Errorf("operation got %v, wanted EINVAL", err) + } + return // Nothing else to do. } - // Try to open the mocked file. - clientHostFile, _, _, err := cFile.Open(0) - if err != nil { - t.Fatalf("open failed: %v", err) + // Should succeed. + if err := fn(backend, f, true); err != nil { + t.Fatalf("operation got %v, wanted nil", err) } - var clientStat syscall.Stat_t - if err := syscall.Fstat(clientHostFile.FD(), &clientStat); err != nil { - t.Fatalf("stat failed: %v", err) + + // Open the directory. + backend.EXPECT().Open(p9.ReadOnly).Times(1) + if _, _, _, err := f.Open(p9.ReadOnly); err != nil { + t.Fatalf("open got %v, wanted nil", err) } - // Compare inode nums to make sure it's the same file. - if clientStat.Ino != osFileStat.Ino { - t.Errorf("fd donation failed") + // Should not work again. + if err := fn(backend, f, false); err != syscall.EINVAL { + t.Fatalf("operation got %v, wanted EINVAL", err) } } -// TestClient is a megatest. -// -// This allows us to probe various edge cases, while changing the state of the -// underlying server in expected ways. The test slowly builds server state and -// is documented inline. -// -// We wind up with the following, after probing edge cases: -// -// FID 1: ServerFile (sf). -// FID 2: Directory (d). -// FID 3: File (f). -// FID 4: Symlink (s). -// -// Although you should use the FID method on the individual files. -func TestClient(t *testing.T) { +func TestCreate(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if !shouldSucceed { + _, _, _, _, err := f.Create("new-file", p9.ReadWrite, 0, 1, 2) + return err + } + + // If the create is going to succeed, then we + // need to create a new backend file, and we + // clone to ensure that we don't close the + // original. + _, newF, err := f.Walk(nil) + if err != nil { + t.Fatalf("clone got %v, wanted nil", err) + } + defer newF.Close() + newBackend := h.Pop(newF) + + // Run a regular FD test to validate that path. + fdTest(t, func(send *fd.FD) *fd.FD { + // Return the send FD on success. + newFile := h.NewFile()(backend) // New file with the parent backend. + newBackend.EXPECT().Create("new-file", p9.ReadWrite, p9.FileMode(0), p9.UID(1), p9.GID(2)).Return(send, newFile, p9.QID{}, uint32(0), nil) + + // Receive the fd back. + recv, _, _, _, err := newF.Create("new-file", p9.ReadWrite, 0, 1, 2) + if err != nil { + t.Fatalf("create got %v, wanted nil", err) + } + return recv + }) + + // The above will fail via normal test flow, so + // we can assume that it passed. + return nil + }) + }) + } +} + +func TestCreateInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + for name := range newTypeMap(nil) { + for _, invalidName := range allInvalidNames(name) { + if _, _, _, _, err := root.Create(invalidName, p9.ReadWrite, 0, 0, 0); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + } + } +} + +func TestMkdir(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if shouldSucceed { + backend.EXPECT().Mkdir("new-directory", p9.FileMode(0), p9.UID(1), p9.GID(2)) + } + _, err := f.Mkdir("new-directory", 0, 1, 2) + return err + }) + }) + } +} + +func TestMkdirInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + for name := range newTypeMap(nil) { + for _, invalidName := range allInvalidNames(name) { + if _, err := root.Mkdir(invalidName, 0, 0, 0); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + } + } +} + +func TestSymlink(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if shouldSucceed { + backend.EXPECT().Symlink("old-name", "new-name", p9.UID(1), p9.GID(2)) + } + _, err := f.Symlink("old-name", "new-name", 1, 2) + return err + }) + }) + } +} + +func TestSyminkInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + for name := range newTypeMap(nil) { + for _, invalidName := range allInvalidNames(name) { + // We need only test for invalid names in the new name, + // the target can be an arbitrary string and we don't + // need to sanity check it. + if _, err := root.Symlink("old-name", invalidName, 0, 0); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + } + } +} + +func TestLink(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if shouldSucceed { + backend.EXPECT().Link(gomock.Any(), "new-link") + } + return f.Link(f, "new-link") + }) + }) + } +} + +func TestLinkInvalid(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + for name := range newTypeMap(nil) { + for _, invalidName := range allInvalidNames(name) { + if err := root.Link(root, invalidName); err != syscall.EINVAL { + t.Errorf("got %v for name %q, want EINVAL", err, invalidName) + } + } + } +} + +func TestMknod(t *testing.T) { + for name := range newTypeMap(nil) { + t.Run(name, func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { + if shouldSucceed { + backend.EXPECT().Mknod("new-block-device", p9.FileMode(0), uint32(1), uint32(2), p9.UID(3), p9.GID(4)).Times(1) + } + _, err := f.Mknod("new-block-device", 0, 1, 2, 3, 4) + return err + }) + }) + } +} + +// concurrentFn is a specification of a concurrent operation. This is used to +// drive the concurrency tests below. +type concurrentFn struct { + name string + match func(p9.FileMode) bool + op func(h *Harness, backend *Mock, f p9.File, callback func()) +} + +func concurrentTest(t *testing.T, name string, fn1, fn2 concurrentFn, sameDir, expectedOkay bool) { var ( - // Sentinel error. - sentinelErr = syscall.Errno(4383) - - // Backend mocks. - a = &AttachMock{} - sf = &FileMock{} - d = &FileMock{} - f = &FileMock{} - s = &FileMock{} - - // Client Files for the above. - sfFile p9.File + names1 []string + names2 []string ) + if sameDir { + // Use the same file one directory up. + names1, names2 = []string{"one", name}, []string{"one", name} + } else { + // For different directories, just use siblings. + names1, names2 = []string{"one", name}, []string{"three", name} + } - testSteps := []struct { - name string - fn func(*p9.Client) error - want error - }{ - { - name: "bad-attach", - want: sentinelErr, - fn: func(c *p9.Client) error { - a.File = nil - a.Err = sentinelErr - _, err := c.Attach("") - return err + t.Run(fmt.Sprintf("%s(%v)+%s(%v)", fn1.name, names1, fn2.name, names2), func(t *testing.T) { + h, c := NewHarness(t) + defer h.Finish() + + _, root := newRoot(h, c) + defer root.Close() + + // Walk to both files as given. + _, f1, err := root.Walk(names1) + if err != nil { + t.Fatalf("error walking, got %v, want nil", err) + } + defer f1.Close() + b1 := h.Pop(f1) + _, f2, err := root.Walk(names2) + if err != nil { + t.Fatalf("error walking, got %v, want nil", err) + } + defer f2.Close() + b2 := h.Pop(f2) + + // Are these a good match for the current test case? + if !fn1.match(b1.Attr.Mode) { + t.SkipNow() + } + if !fn2.match(b2.Attr.Mode) { + t.SkipNow() + } + + // Construct our "concurrency creator". + in1 := make(chan struct{}, 1) + in2 := make(chan struct{}, 1) + var top sync.WaitGroup + var fns sync.WaitGroup + defer top.Wait() + top.Add(2) // Accounting for below. + defer fns.Done() + fns.Add(1) // See line above; released before top.Wait. + go func() { + defer top.Done() + fn1.op(h, b1, f1, func() { + in1 <- struct{}{} + fns.Wait() + }) + }() + go func() { + defer top.Done() + fn2.op(h, b2, f2, func() { + in2 <- struct{}{} + fns.Wait() + }) + }() + + // Compute a reasonable timeout. If we expect the operation to hang, + // give it 10 milliseconds before we assert that it's fine. After all, + // there will be a lot of these tests. If we don't expect it to hang, + // give it a full minute, since the machine could be slow. + timeout := 10 * time.Millisecond + if expectedOkay { + timeout = 1 * time.Minute + } + + // Read the first channel. + var second chan struct{} + select { + case <-in1: + second = in2 + case <-in2: + second = in1 + } + + // Catch concurrency. + select { + case <-second: + // We finished successful. Is this good? Depends on the + // expected result. + if !expectedOkay { + t.Errorf("%q and %q proceeded concurrently!", fn1.name, fn2.name) + } + case <-time.After(timeout): + // Great, things did not proceed concurrently. Is that what we + // expected? + if expectedOkay { + t.Errorf("%q and %q hung concurrently!", fn1.name, fn2.name) + } + } + }) +} + +func randomFileName() string { + return fmt.Sprintf("%x", rand.Int63()) +} + +func TestConcurrency(t *testing.T) { + readExclusive := []concurrentFn{ + { + // N.B. We can't explicitly check WalkGetAttr behavior, + // but we rely on the fact that the internal code paths + // are the same. + name: "walk", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + // See the documentation of WalkCallback. + // Because walk is actually implemented by the + // mock, we need a special place for this + // callback. + // + // Note that a clone actually locks the parent + // node. So we walk from this node to test + // concurrent operations appropriately. + backend.WalkCallback = func() error { + callback() + return nil + } + f.Walk([]string{randomFileName()}) // Won't exist. }, }, { - name: "attach", - fn: func(c *p9.Client) error { - a.Called = false - a.File = sf - a.Err = nil - // The attached root must have a valid mode. - sf.GetAttrMock.Attr = p9.Attr{Mode: p9.ModeDirectory} - sf.GetAttrMock.Valid = p9.AttrMask{Mode: true} - var err error - sfFile, err = c.Attach("") - if !a.Called { - t.Errorf("Attach never Called?") - } - return err + name: "fsync", + match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Open(gomock.Any()) + backend.EXPECT().FSync().Do(func() { + callback() + }) + f.Open(p9.ReadOnly) // Required. + f.FSync() }, }, { - name: "bad-walk", - want: sentinelErr, - fn: func(c *p9.Client) error { - // Walk only called when WalkGetAttr not available. - sf.WalkGetAttrMock.Err = syscall.ENOSYS - sf.WalkMock.File = d - sf.WalkMock.Err = sentinelErr - _, _, err := sfFile.Walk([]string{"foo", "bar"}) - return err + name: "readdir", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Open(gomock.Any()) + backend.EXPECT().Readdir(gomock.Any(), gomock.Any()).Do(func(uint64, uint32) { + callback() + }) + f.Open(p9.ReadOnly) // Required. + f.Readdir(0, 1) }, }, { - name: "walk-to-dir", - fn: func(c *p9.Client) error { - // Walk only called when WalkGetAttr not available. - sf.WalkGetAttrMock.Err = syscall.ENOSYS - sf.WalkMock.Called = false - sf.WalkMock.Names = nil - sf.WalkMock.File = d - sf.WalkMock.Err = nil - sf.WalkMock.QIDs = []p9.QID{{Type: 1}} - // All intermediate values must be directories. - d.WalkGetAttrMock.Err = syscall.ENOSYS - d.WalkMock.Called = false - d.WalkMock.Names = nil - d.WalkMock.File = d // Walk to self. - d.WalkMock.Err = nil - d.WalkMock.QIDs = []p9.QID{{Type: 1}} - d.GetAttrMock.Attr = p9.Attr{Mode: p9.ModeDirectory} - d.GetAttrMock.Valid = p9.AttrMask{Mode: true} - var qids []p9.QID - var err error - qids, _, err = sfFile.Walk([]string{"foo", "bar"}) - if !sf.WalkMock.Called { - t.Errorf("Walk never Called?") - } - if !d.GetAttrMock.Called { - t.Errorf("GetAttr never Called?") - } - if !reflect.DeepEqual(sf.WalkMock.Names, []string{"foo"}) { - t.Errorf("got names %v wanted []{foo}", sf.WalkMock.Names) - } - if !reflect.DeepEqual(d.WalkMock.Names, []string{"bar"}) { - t.Errorf("got names %v wanted []{bar}", d.WalkMock.Names) - } - if len(qids) != 2 || qids[len(qids)-1].Type != 1 { - t.Errorf("got qids %v wanted []{..., {Type: 1}}", qids) - } - return err + name: "readlink", + match: func(mode p9.FileMode) bool { return mode.IsSymlink() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Readlink().Do(func() { + callback() + }) + f.Readlink() }, }, { - name: "walkgetattr-to-dir", - fn: func(c *p9.Client) error { - sf.WalkGetAttrMock.Called = false - sf.WalkGetAttrMock.Names = nil - sf.WalkGetAttrMock.File = d - sf.WalkGetAttrMock.Err = nil - sf.WalkGetAttrMock.QIDs = []p9.QID{{Type: 1}} - sf.WalkGetAttrMock.Attr = p9.Attr{Mode: p9.ModeDirectory, UID: 1} - sf.WalkGetAttrMock.Valid = p9.AttrMask{Mode: true} - // See above. - d.WalkGetAttrMock.Called = false - d.WalkGetAttrMock.Names = nil - d.WalkGetAttrMock.File = d // Walk to self. - d.WalkGetAttrMock.Err = nil - d.WalkGetAttrMock.QIDs = []p9.QID{{Type: 1}} - d.WalkGetAttrMock.Attr = p9.Attr{Mode: p9.ModeDirectory, UID: 1} - d.WalkGetAttrMock.Valid = p9.AttrMask{Mode: true} - var qids []p9.QID - var err error - var mask p9.AttrMask - var attr p9.Attr - qids, _, mask, attr, err = sfFile.WalkGetAttr([]string{"foo", "bar"}) - if !sf.WalkGetAttrMock.Called { - t.Errorf("Walk never Called?") - } - if !reflect.DeepEqual(sf.WalkGetAttrMock.Names, []string{"foo"}) { - t.Errorf("got names %v wanted []{foo}", sf.WalkGetAttrMock.Names) - } - if !reflect.DeepEqual(d.WalkGetAttrMock.Names, []string{"bar"}) { - t.Errorf("got names %v wanted []{bar}", d.WalkGetAttrMock.Names) - } - if len(qids) != 2 || qids[len(qids)-1].Type != 1 { - t.Errorf("got qids %v wanted []{..., {Type: 1}}", qids) - } - if !reflect.DeepEqual(attr, sf.WalkGetAttrMock.Attr) { - t.Errorf("got attrs %s wanted %s", attr, sf.WalkGetAttrMock.Attr) - } - if !reflect.DeepEqual(mask, sf.WalkGetAttrMock.Valid) { - t.Errorf("got mask %s wanted %s", mask, sf.WalkGetAttrMock.Valid) - } - return err + name: "connect", + match: func(mode p9.FileMode) bool { return mode.IsSocket() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Connect(gomock.Any()).Do(func(p9.ConnectFlags) { + callback() + }) + f.Connect(0) }, }, { - name: "walk-to-file", - fn: func(c *p9.Client) error { - // Basic sanity check is done in walk-to-dir. - // - // Here we just create basic file FIDs to use. - sf.WalkMock.File = f - sf.WalkMock.Err = nil - var err error - _, _, err = sfFile.Walk(nil) - return err + name: "open", + match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Open(gomock.Any()).Do(func(p9.OpenFlags) { + callback() + }) + f.Open(p9.ReadOnly) }, }, { - name: "walk-to-symlink", - fn: func(c *p9.Client) error { - // See note in walk-to-file. - sf.WalkMock.File = s - sf.WalkMock.Err = nil - var err error - _, _, err = sfFile.Walk(nil) - return err + name: "flush", + match: func(mode p9.FileMode) bool { return true }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Flush().Do(func() { + callback() + }) + f.Flush() + }, + }, + } + writeExclusive := []concurrentFn{ + { + // N.B. We can't really check getattr. But this is an + // extremely low-risk function, it seems likely that + // this check is paranoid anyways. + name: "setattr", + match: func(mode p9.FileMode) bool { return true }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().SetAttr(gomock.Any(), gomock.Any()).Do(func(p9.SetAttrMask, p9.SetAttr) { + callback() + }) + f.SetAttr(p9.SetAttrMask{}, p9.SetAttr{}) }, }, { - name: "bad-statfs", - want: sentinelErr, - fn: func(c *p9.Client) error { - sf.StatFSMock.Err = sentinelErr - _, err := sfFile.StatFS() - return err + name: "unlinkAt", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Do(func(string, uint32) { + callback() + }) + f.UnlinkAt(randomFileName(), 0) }, }, { - name: "statfs", - fn: func(c *p9.Client) error { - sf.StatFSMock.Called = false - sf.StatFSMock.Stat = p9.FSStat{Type: 1} - sf.StatFSMock.Err = nil - stat, err := sfFile.StatFS() - if !sf.StatFSMock.Called { - t.Errorf("StatfS never Called?") - } - if stat.Type != 1 { - t.Errorf("got stat %v wanted {Type: 1}", stat) - } - return err + name: "mknod", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Mknod(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.FileMode, uint32, uint32, p9.UID, p9.GID) { + callback() + }) + f.Mknod(randomFileName(), 0, 0, 0, 0, 0) + }, + }, + { + name: "link", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Link(gomock.Any(), gomock.Any()).Do(func(p9.File, string) { + callback() + }) + f.Link(f, randomFileName()) + }, + }, + { + name: "symlink", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Symlink(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, string, p9.UID, p9.GID) { + callback() + }) + f.Symlink(randomFileName(), randomFileName(), 0, 0) + }, + }, + { + name: "mkdir", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().Mkdir(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.FileMode, p9.UID, p9.GID) { + callback() + }) + f.Mkdir(randomFileName(), 0, 0, 0) + }, + }, + { + name: "create", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + // Return an error for the creation operation, as this is the simplest. + backend.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, p9.QID{}, uint32(0), syscall.EINVAL).Do(func(string, p9.OpenFlags, p9.FileMode, p9.UID, p9.GID) { + callback() + }) + f.Create(randomFileName(), p9.ReadOnly, 0, 0, 0) }, }, } + globalExclusive := []concurrentFn{ + { + name: "remove", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + // Remove operates on a locked parent. So we + // add a child, walk to it and call remove. + // Note that because this operation can operate + // concurrently with itself, we need to + // generate a random file name. + randomFile := randomFileName() + backend.AddChild(randomFile, h.NewFile()) + defer backend.RemoveChild(randomFile) + _, file, err := f.Walk([]string{randomFile}) + if err != nil { + h.t.Fatalf("walk got %v, want nil", err) + } - // First, create a new server and connection. - serverSocket, clientSocket, err := unet.SocketPair(false) - if err != nil { - t.Fatalf("socketpair got err %v wanted nil", err) - } - defer clientSocket.Close() - server := p9.NewServer(a) - go server.Handle(serverSocket) - client, err := p9.NewClient(clientSocket, 1024*1024 /* 1M message size */, p9.HighestVersionString()) - if err != nil { - t.Fatalf("new client got err %v, wanted nil", err) - } + // Remove is automatically translated to the parent. + backend.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Do(func(string, uint32) { + callback() + }) - // Now, run through each of the test steps. - for _, step := range testSteps { - err := step.fn(client) - if err != step.want { - // Don't fail, just note this one step failed. - t.Errorf("step %q got %v wanted %v", step.name, err, step.want) - } - } -} + // Remove is also a close. + file.(deprecatedRemover).Remove() + }, + }, + { + name: "rename", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + // Similarly to remove, because we need to + // operate on a child, we allow a walk. + randomFile := randomFileName() + backend.AddChild(randomFile, h.NewFile()) + defer backend.RemoveChild(randomFile) + _, file, err := f.Walk([]string{randomFile}) + if err != nil { + h.t.Fatalf("walk got %v, want nil", err) + } + defer file.Close() + fileBackend := h.Pop(file) -func BenchmarkClient(b *testing.B) { - // Backend mock. - a := &AttachMock{ - File: &FileMock{ - ReadAtMock: ReadAtMock{N: 1}, + // Rename is automatically translated to the parent. + backend.EXPECT().RenameAt(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.File, string) { + callback() + }) + + // Attempt the rename. + fileBackend.EXPECT().Renamed(gomock.Any(), gomock.Any()) + file.Rename(f, randomFileName()) + }, }, - } + { + name: "renameAt", + match: func(mode p9.FileMode) bool { return mode.IsDir() }, + op: func(h *Harness, backend *Mock, f p9.File, callback func()) { + backend.EXPECT().RenameAt(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.File, string) { + callback() + }) - // First, create a new server and connection. - serverSocket, clientSocket, err := unet.SocketPair(false) - if err != nil { - b.Fatalf("socketpair got err %v wanted nil", err) - } - defer clientSocket.Close() - server := p9.NewServer(a) - go server.Handle(serverSocket) - client, err := p9.NewClient(clientSocket, 1024*1024 /* 1M message size */, p9.HighestVersionString()) - if err != nil { - b.Fatalf("new client got %v, expected nil", err) + // Attempt the rename. There are no active fids + // with this name, so we don't need to expect + // Renamed hooks on anything. + f.RenameAt(randomFileName(), f, randomFileName()) + }, + }, } - // Attach to the server. - f, err := client.Attach("") - if err != nil { - b.Fatalf("error during attach, got %v wanted nil", err) + for _, fn1 := range readExclusive { + for _, fn2 := range readExclusive { + for name := range newTypeMap(nil) { + // Everything should be able to proceed in parallel. + concurrentTest(t, name, fn1, fn2, true, true) + concurrentTest(t, name, fn1, fn2, false, true) + } + } } - // Open the file. - if _, _, _, err := f.Open(p9.ReadOnly); err != nil { - b.Fatalf("error during open, got %v wanted nil", err) + for _, fn1 := range append(readExclusive, writeExclusive...) { + for _, fn2 := range writeExclusive { + for name := range newTypeMap(nil) { + // Only cross-directory functions should proceed in parallel. + concurrentTest(t, name, fn1, fn2, true, false) + concurrentTest(t, name, fn1, fn2, false, true) + } + } } - // Reset the clock. - b.ResetTimer() - - // Do N reads. - var buf [1]byte - for i := 0; i < b.N; i++ { - _, err := f.ReadAt(buf[:], 0) - if err != nil { - b.Fatalf("error during read %d, got %v wanted nil", i, err) + for _, fn1 := range append(append(readExclusive, writeExclusive...), globalExclusive...) { + for _, fn2 := range globalExclusive { + for name := range newTypeMap(nil) { + // Nothing should be able to run in parallel. + concurrentTest(t, name, fn1, fn2, true, false) + concurrentTest(t, name, fn1, fn2, false, false) + } } } } diff --git a/pkg/p9/p9test/mocks.go b/pkg/p9/p9test/mocks.go deleted file mode 100644 index 9a8c14975..000000000 --- a/pkg/p9/p9test/mocks.go +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 p9test - -import ( - "gvisor.googlesource.com/gvisor/pkg/fd" - "gvisor.googlesource.com/gvisor/pkg/p9" -) - -// StatFSMock mocks p9.File.StatFS. -type StatFSMock struct { - Called bool - - // Return. - Stat p9.FSStat - Err error -} - -// StatFS implements p9.File.StatFS. -func (f *StatFSMock) StatFS() (p9.FSStat, error) { - f.Called = true - return f.Stat, f.Err -} - -// GetAttrMock mocks p9.File.GetAttr. -type GetAttrMock struct { - Called bool - - // Args. - Req p9.AttrMask - - // Return. - QID p9.QID - Valid p9.AttrMask - Attr p9.Attr - Err error -} - -// GetAttr implements p9.File.GetAttr. -func (g *GetAttrMock) GetAttr(req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) { - g.Called, g.Req = true, req - return g.QID, g.Valid, g.Attr, g.Err -} - -// WalkGetAttrMock mocks p9.File.WalkGetAttr. -type WalkGetAttrMock struct { - Called bool - - // Args. - Names []string - - // Return. - QIDs []p9.QID - File p9.File - Valid p9.AttrMask - Attr p9.Attr - Err error -} - -// WalkGetAttr implements p9.File.WalkGetAttr. -func (w *WalkGetAttrMock) WalkGetAttr(names []string) ([]p9.QID, p9.File, p9.AttrMask, p9.Attr, error) { - w.Called = true - w.Names = append(w.Names, names...) - return w.QIDs, w.File, w.Valid, w.Attr, w.Err -} - -// SetAttrMock mocks p9.File.SetAttr. -type SetAttrMock struct { - Called bool - - // Args. - Valid p9.SetAttrMask - Attr p9.SetAttr - - // Return. - Err error -} - -// SetAttr implements p9.File.SetAttr. -func (s *SetAttrMock) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error { - s.Called, s.Valid, s.Attr = true, valid, attr - return s.Err -} - -// RemoveMock mocks p9.File.Remove. -type RemoveMock struct { - Called bool - - // Return. - Err error -} - -// Remove implements p9.File.Remove. -func (r *RemoveMock) Remove() error { - r.Called = true - return r.Err -} - -// OpenMock mocks p9.File.Open. -type OpenMock struct { - Called bool - - // Args. - Flags p9.OpenFlags - - // Return. - File *fd.FD - QID p9.QID - IOUnit uint32 - Err error -} - -// Open implements p9.File.Open. -func (o *OpenMock) Open(flags p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) { - o.Called, o.Flags = true, flags - return o.File, o.QID, o.IOUnit, o.Err -} - -// ReadAtMock mocks p9.File.ReadAt. -type ReadAtMock struct { - Called bool - - // Args. - P []byte - Offset uint64 - - // Return. - N int - Err error -} - -// ReadAt implements p9.File.ReadAt. -func (r *ReadAtMock) ReadAt(p []byte, offset uint64) (int, error) { - r.Called, r.P, r.Offset = true, p, offset - return r.N, r.Err -} - -// WriteAtMock mocks p9.File.WriteAt. -type WriteAtMock struct { - Called bool - - // Args. - P []byte - Offset uint64 - - // Return. - N int - Err error -} - -// WriteAt implements p9.File.WriteAt. -func (w *WriteAtMock) WriteAt(p []byte, offset uint64) (int, error) { - w.Called, w.P, w.Offset = true, p, offset - return w.N, w.Err -} - -// FSyncMock mocks p9.File.FSync. -type FSyncMock struct { - Called bool - - // Return. - Err error -} - -// FSync implements p9.File.FSync. -func (f *FSyncMock) FSync() error { - f.Called = true - return f.Err -} - -// MkdirMock mocks p9.File.Mkdir. -type MkdirMock struct { - Called bool - - // Args. - Name string - Permissions p9.FileMode - UID p9.UID - GID p9.GID - - // Return. - QID p9.QID - Err error -} - -// Mkdir implements p9.File.Mkdir. -func (s *MkdirMock) Mkdir(name string, permissions p9.FileMode, uid p9.UID, gid p9.GID) (p9.QID, error) { - s.Called, s.Name, s.Permissions, s.UID, s.GID = true, name, permissions, uid, gid - return s.QID, s.Err -} - -// SymlinkMock mocks p9.File.Symlink. -type SymlinkMock struct { - Called bool - - // Args. - Oldname string - Newname string - UID p9.UID - GID p9.GID - - // Return. - QID p9.QID - Err error -} - -// Symlink implements p9.File.Symlink. -func (s *SymlinkMock) Symlink(oldname string, newname string, uid p9.UID, gid p9.GID) (p9.QID, error) { - s.Called, s.Oldname, s.Newname, s.UID, s.GID = true, oldname, newname, uid, gid - return s.QID, s.Err -} - -// MknodMock mocks p9.File.Mknod. -type MknodMock struct { - Called bool - - // Args. - Name string - Permissions p9.FileMode - Major uint32 - Minor uint32 - UID p9.UID - GID p9.GID - - // Return. - QID p9.QID - Err error -} - -// Mknod implements p9.File.Mknod. -func (m *MknodMock) Mknod(name string, permissions p9.FileMode, major uint32, minor uint32, uid p9.UID, gid p9.GID) (p9.QID, error) { - m.Called, m.Name, m.Permissions, m.Major, m.Minor, m.UID, m.GID = true, name, permissions, major, minor, uid, gid - return m.QID, m.Err -} - -// UnlinkAtMock mocks p9.File.UnlinkAt. -type UnlinkAtMock struct { - Called bool - - // Args. - Name string - Flags uint32 - - // Return. - Err error -} - -// UnlinkAt implements p9.File.UnlinkAt. -func (u *UnlinkAtMock) UnlinkAt(name string, flags uint32) error { - u.Called, u.Name, u.Flags = true, name, flags - return u.Err -} - -// ReaddirMock mocks p9.File.Readdir. -type ReaddirMock struct { - Called bool - - // Args. - Offset uint64 - Count uint32 - - // Return. - Dirents []p9.Dirent - Err error -} - -// Readdir implements p9.File.Readdir. -func (r *ReaddirMock) Readdir(offset uint64, count uint32) ([]p9.Dirent, error) { - r.Called, r.Offset, r.Count = true, offset, count - return r.Dirents, r.Err -} - -// ReadlinkMock mocks p9.File.Readlink. -type ReadlinkMock struct { - Called bool - - // Return. - Target string - Err error -} - -// Readlink implements p9.File.Readlink. -func (r *ReadlinkMock) Readlink() (string, error) { - r.Called = true - return r.Target, r.Err -} - -// AttachMock mocks p9.Attacher.Attach. -type AttachMock struct { - Called bool - - // Return. - File p9.File - Err error -} - -// Attach implements p9.Attacher.Attach. -func (a *AttachMock) Attach() (p9.File, error) { - a.Called = true - return a.File, a.Err -} - -// WalkMock mocks p9.File.Walk. -type WalkMock struct { - Called bool - - // Args. - Names []string - - // Return. - QIDs []p9.QID - File p9.File - Err error -} - -// Walk implements p9.File.Walk. -func (w *WalkMock) Walk(names []string) ([]p9.QID, p9.File, error) { - w.Called = true - w.Names = append(w.Names, names...) - return w.QIDs, w.File, w.Err -} - -// RenameMock mocks p9.File.Rename. -type RenameMock struct { - Called bool - - // Args. - Directory p9.File - Name string - - // Return. - Err error -} - -// Rename implements p9.File.Rename. -func (r *RenameMock) Rename(directory p9.File, name string) error { - r.Called, r.Directory, r.Name = true, directory, name - return r.Err -} - -// CloseMock mocks p9.File.Close. -type CloseMock struct { - Called bool - - // Return. - Err error -} - -// Close implements p9.File.Close. -func (d *CloseMock) Close() error { - d.Called = true - return d.Err -} - -// CreateMock mocks p9.File.Create. -type CreateMock struct { - Called bool - - // Args. - Name string - Flags p9.OpenFlags - Permissions p9.FileMode - UID p9.UID - GID p9.GID - - // Return. - HostFile *fd.FD - File p9.File - QID p9.QID - IOUnit uint32 - Err error -} - -// Create implements p9.File.Create. -func (c *CreateMock) Create(name string, flags p9.OpenFlags, permissions p9.FileMode, uid p9.UID, gid p9.GID) (*fd.FD, p9.File, p9.QID, uint32, error) { - c.Called, c.Name, c.Flags, c.Permissions, c.UID, c.GID = true, name, flags, permissions, uid, gid - return c.HostFile, c.File, c.QID, c.IOUnit, c.Err -} - -// LinkMock mocks p9.File.Link. -type LinkMock struct { - Called bool - - // Args. - Target p9.File - Newname string - - // Return. - Err error -} - -// Link implements p9.File.Link. -func (l *LinkMock) Link(target p9.File, newname string) error { - l.Called, l.Target, l.Newname = true, target, newname - return l.Err -} - -// RenameAtMock mocks p9.File.RenameAt. -type RenameAtMock struct { - Called bool - - // Args. - Oldname string - Newdir p9.File - Newname string - - // Return. - Err error -} - -// RenameAt implements p9.File.RenameAt. -func (r *RenameAtMock) RenameAt(oldname string, newdir p9.File, newname string) error { - r.Called, r.Oldname, r.Newdir, r.Newname = true, oldname, newdir, newname - return r.Err -} - -// FlushMock mocks p9.File.Flush. -type FlushMock struct { - Called bool - - // Return. - Err error -} - -// Flush implements p9.File.Flush. -func (f *FlushMock) Flush() error { - return f.Err -} - -// ConnectMock mocks p9.File.Connect. -type ConnectMock struct { - Called bool - - // Args. - Flags p9.ConnectFlags - - // Return. - File *fd.FD - Err error -} - -// Connect implements p9.File.Connect. -func (o *ConnectMock) Connect(flags p9.ConnectFlags) (*fd.FD, error) { - o.Called, o.Flags = true, flags - return o.File, o.Err -} - -// FileMock mocks p9.File. -type FileMock struct { - WalkMock - WalkGetAttrMock - StatFSMock - GetAttrMock - SetAttrMock - RemoveMock - RenameMock - CloseMock - OpenMock - ReadAtMock - WriteAtMock - FSyncMock - CreateMock - MkdirMock - SymlinkMock - LinkMock - MknodMock - RenameAtMock - UnlinkAtMock - ReaddirMock - ReadlinkMock - FlushMock - ConnectMock -} - -var ( - _ p9.File = &FileMock{} -) diff --git a/pkg/p9/p9test/p9test.go b/pkg/p9/p9test/p9test.go new file mode 100644 index 000000000..417b55950 --- /dev/null +++ b/pkg/p9/p9test/p9test.go @@ -0,0 +1,329 @@ +// Copyright 2018 Google Inc. +// +// 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 p9test provides standard mocks for p9. +package p9test + +import ( + "fmt" + "sync" + "sync/atomic" + "syscall" + "testing" + + "github.com/golang/mock/gomock" + "gvisor.googlesource.com/gvisor/pkg/p9" + "gvisor.googlesource.com/gvisor/pkg/unet" +) + +// Harness is an attacher mock. +type Harness struct { + t *testing.T + mockCtrl *gomock.Controller + Attacher *MockAttacher + wg sync.WaitGroup + clientSocket *unet.Socket + mu sync.Mutex + created []*Mock +} + +// globalPath is a QID.Path Generator. +var globalPath uint64 + +// MakePath returns a globally unique path. +func MakePath() uint64 { + return atomic.AddUint64(&globalPath, 1) +} + +// Generator is a function that generates a new file. +type Generator func(parent *Mock) *Mock + +// Mock is a common mock element. +type Mock struct { + p9.DefaultWalkGetAttr + *MockFile + parent *Mock + closed bool + harness *Harness + QID p9.QID + Attr p9.Attr + children map[string]Generator + + // WalkCallback is a special function that will be called from within + // the walk context. This is needed for the concurrent tests within + // this package. + WalkCallback func() error +} + +// globalMu protects the children maps in all mocks. Note that this is not a +// particularly elegant solution, but because the test has walks from the root +// through to final nodes, we must share maps below, and it's easiest to simply +// protect against concurrent access globally. +var globalMu sync.RWMutex + +// AddChild adds a new child to the Mock. +func (m *Mock) AddChild(name string, generator Generator) { + globalMu.Lock() + defer globalMu.Unlock() + m.children[name] = generator +} + +// RemoveChild removes the child with the given name. +func (m *Mock) RemoveChild(name string) { + globalMu.Lock() + defer globalMu.Unlock() + delete(m.children, name) +} + +// Matches implements gomock.Matcher.Matches. +func (m *Mock) Matches(x interface{}) bool { + if om, ok := x.(*Mock); ok { + return m.QID.Path == om.QID.Path + } + return false +} + +// String implements gomock.Matcher.String. +func (m *Mock) String() string { + return fmt.Sprintf("Mock{Mode: 0x%x, QID.Path: %d}", m.Attr.Mode, m.QID.Path) +} + +// GetAttr returns the current attributes. +func (m *Mock) GetAttr(mask p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) { + return m.QID, p9.AttrMaskAll(), m.Attr, nil +} + +// Walk supports clone and walking in directories. +func (m *Mock) Walk(names []string) ([]p9.QID, p9.File, error) { + if m.WalkCallback != nil { + if err := m.WalkCallback(); err != nil { + return nil, nil, err + } + } + if len(names) == 0 { + // Clone the file appropriately. + nm := m.harness.NewMock(m.parent, m.QID.Path, m.Attr) + nm.children = m.children // Inherit children. + return []p9.QID{nm.QID}, nm, nil + } else if len(names) != 1 { + m.harness.t.Fail() // Should not happen. + return nil, nil, syscall.EINVAL + } + + if m.Attr.Mode.IsDir() { + globalMu.RLock() + defer globalMu.RUnlock() + if fn, ok := m.children[names[0]]; ok { + // Generate the child. + nm := fn(m) + return []p9.QID{nm.QID}, nm, nil + } + // No child found. + return nil, nil, syscall.ENOENT + } + + // Call the underlying mock. + return m.MockFile.Walk(names) +} + +// WalkGetAttr calls the default implementation; this is a client-side optimization. +func (m *Mock) WalkGetAttr(names []string) ([]p9.QID, p9.File, p9.AttrMask, p9.Attr, error) { + return m.DefaultWalkGetAttr.WalkGetAttr(names) +} + +// Pop pops off the most recently created Mock and assert that this mock +// represents the same file passed in. If nil is passed in, no check is +// performed. +// +// Precondition: there must be at least one Mock or this will panic. +func (h *Harness) Pop(clientFile p9.File) *Mock { + h.mu.Lock() + defer h.mu.Unlock() + + if clientFile == nil { + // If no clientFile is provided, then we always return the last + // created file. The caller can safely use this as long as + // there is no concurrency. + m := h.created[len(h.created)-1] + h.created = h.created[:len(h.created)-1] + return m + } + + qid, _, _, err := clientFile.GetAttr(p9.AttrMaskAll()) + if err != nil { + // We do not expect this to happen. + panic(fmt.Sprintf("err during Pop: %v", err)) + } + + // Find the relevant file in our created list. We must scan the last + // from back to front to ensure that we favor the most recently + // generated file. + for i := len(h.created) - 1; i >= 0; i-- { + m := h.created[i] + if qid.Path == m.QID.Path { + // Copy and truncate. + copy(h.created[i:], h.created[i+1:]) + h.created = h.created[:len(h.created)-1] + return m + } + } + + // Unable to find relevant file. + panic(fmt.Sprintf("unable to locate file with QID %+v", qid.Path)) +} + +// NewMock returns a new base file. +func (h *Harness) NewMock(parent *Mock, path uint64, attr p9.Attr) *Mock { + m := &Mock{ + MockFile: NewMockFile(h.mockCtrl), + parent: parent, + harness: h, + QID: p9.QID{ + Type: p9.QIDType((attr.Mode & p9.FileModeMask) >> 12), + Path: path, + }, + Attr: attr, + } + + // Always ensure Close is after the parent's close. Note that this + // can't be done via a straight-forward After call, because the parent + // might change after initial creation. We ensure that this is true at + // close time. + m.EXPECT().Close().Return(nil).Times(1).Do(func() { + if m.parent != nil && m.parent.closed { + h.t.FailNow() + } + // Note that this should not be racy, as this operation should + // be protected by the Times(1) above first. + m.closed = true + }) + + // Remember what was created. + h.mu.Lock() + defer h.mu.Unlock() + h.created = append(h.created, m) + + return m +} + +// NewFile returns a new file mock. +// +// Note that ReadAt and WriteAt must be mocked separately. +func (h *Harness) NewFile() Generator { + return func(parent *Mock) *Mock { + return h.NewMock(parent, MakePath(), p9.Attr{Mode: p9.ModeRegular}) + } +} + +// NewDirectory returns a new mock directory. +// +// Note that Mkdir, Link, Mknod, RenameAt, UnlinkAt and Readdir must be mocked +// separately. Walk is provided and children may be manipulated via AddChild +// and RemoveChild. After calling Walk remotely, one can use Pop to find the +// corresponding backend mock on the server side. +func (h *Harness) NewDirectory(contents map[string]Generator) Generator { + return func(parent *Mock) *Mock { + m := h.NewMock(parent, MakePath(), p9.Attr{Mode: p9.ModeDirectory}) + m.children = contents // Save contents. + return m + } +} + +// NewSymlink returns a new mock directory. +// +// Note that Readlink must be mocked separately. +func (h *Harness) NewSymlink() Generator { + return func(parent *Mock) *Mock { + return h.NewMock(parent, MakePath(), p9.Attr{Mode: p9.ModeSymlink}) + } +} + +// NewBlockDevice returns a new mock block device. +func (h *Harness) NewBlockDevice() Generator { + return func(parent *Mock) *Mock { + return h.NewMock(parent, MakePath(), p9.Attr{Mode: p9.ModeBlockDevice}) + } +} + +// NewCharacterDevice returns a new mock character device. +func (h *Harness) NewCharacterDevice() Generator { + return func(parent *Mock) *Mock { + return h.NewMock(parent, MakePath(), p9.Attr{Mode: p9.ModeCharacterDevice}) + } +} + +// NewNamedPipe returns a new mock named pipe. +func (h *Harness) NewNamedPipe() Generator { + return func(parent *Mock) *Mock { + return h.NewMock(parent, MakePath(), p9.Attr{Mode: p9.ModeNamedPipe}) + } +} + +// NewSocket returns a new mock socket. +func (h *Harness) NewSocket() Generator { + return func(parent *Mock) *Mock { + return h.NewMock(parent, MakePath(), p9.Attr{Mode: p9.ModeSocket}) + } +} + +// Finish completes all checks and shuts down the server. +func (h *Harness) Finish() { + h.clientSocket.Close() + h.wg.Wait() + h.mockCtrl.Finish() +} + +// NewHarness creates and returns a new test server. +// +// It should always be used as: +// +// h, c := NewHarness(t) +// defer h.Finish() +// +func NewHarness(t *testing.T) (*Harness, *p9.Client) { + // Create the mock. + mockCtrl := gomock.NewController(t) + h := &Harness{ + t: t, + mockCtrl: mockCtrl, + Attacher: NewMockAttacher(mockCtrl), + } + + // Make socket pair. + serverSocket, clientSocket, err := unet.SocketPair(false) + if err != nil { + t.Fatalf("socketpair got err %v wanted nil", err) + } + + // Start the server, synchronized on exit. + server := p9.NewServer(h.Attacher) + h.wg.Add(1) + go func() { + defer h.wg.Done() + server.Handle(serverSocket) + }() + + // Create the client. + client, err := p9.NewClient(clientSocket, 1024, p9.HighestVersionString()) + if err != nil { + serverSocket.Close() + clientSocket.Close() + t.Fatalf("new client got %v, expected nil", err) + return nil, nil // Never hit. + } + + // Capture the client socket. + h.clientSocket = clientSocket + return h, client +} diff --git a/pkg/p9/path_tree.go b/pkg/p9/path_tree.go new file mode 100644 index 000000000..97f90bcd5 --- /dev/null +++ b/pkg/p9/path_tree.go @@ -0,0 +1,109 @@ +// Copyright 2018 Google Inc. +// +// 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 p9 + +import ( + "fmt" + "sync" +) + +// pathNode is a single node in a path traversal. +// +// These are shared by all fidRefs that point to the same path. +// +// These are not synchronized because we allow certain operations (file walk) +// to proceed without having to acquire a write lock. The lock in this +// structure exists to synchronize high-level, semantic operations, such as the +// simultaneous creation and deletion of a file. +// +// (+) below is the path component string. +type pathNode struct { + mu sync.RWMutex // See above. + fidRefs sync.Map // => map[*fidRef]string(+) + children sync.Map // => map[string(+)]*pathNode + count int64 +} + +// pathNodeFor returns the path node for the given name, or a new one. +// +// Precondition: mu must be held in a readable fashion. +func (p *pathNode) pathNodeFor(name string) *pathNode { + // Load the existing path node. + if pn, ok := p.children.Load(name); ok { + return pn.(*pathNode) + } + + // Create a new pathNode for shared use. + pn, _ := p.children.LoadOrStore(name, new(pathNode)) + return pn.(*pathNode) +} + +// nameFor returns the name for the given fidRef. +// +// Precondition: mu must be held in a readable fashion. +func (p *pathNode) nameFor(ref *fidRef) string { + if s, ok := p.fidRefs.Load(ref); ok { + return s.(string) + } + + // This should not happen, don't proceed. + panic(fmt.Sprintf("expected name for %+v, none found", ref)) +} + +// addChild adds a child to the given pathNode. +// +// This applies only to an individual fidRef. +// +// Precondition: mu must be held in a writable fashion. +func (p *pathNode) addChild(ref *fidRef, name string) { + if s, ok := p.fidRefs.Load(ref); ok { + // This should not happen, don't proceed. + panic(fmt.Sprintf("unexpected fidRef %+v with path %q, wanted %q", ref, s, name)) + } + + p.fidRefs.Store(ref, name) +} + +// removeChild removes the given child. +// +// This applies only to an individual fidRef. +// +// Precondition: mu must be held in a writable fashion. +func (p *pathNode) removeChild(ref *fidRef) { + p.fidRefs.Delete(ref) +} + +// removeWithName removes all references with the given name. +// +// The original pathNode is returned by this function, and removed from this +// pathNode. Any operations on the removed tree must use this value. +// +// The provided function is executed after removal. +// +// Precondition: mu must be held in a writable fashion. +func (p *pathNode) removeWithName(name string, fn func(ref *fidRef)) *pathNode { + p.fidRefs.Range(func(key, value interface{}) bool { + if value.(string) == name { + p.fidRefs.Delete(key) + fn(key.(*fidRef)) + } + return true + }) + + // Return the original path node. + origPathNode := p.pathNodeFor(name) + p.children.Delete(name) + return origPathNode +} diff --git a/pkg/p9/server.go b/pkg/p9/server.go index 5c7cb18c8..3ef151595 100644 --- a/pkg/p9/server.go +++ b/pkg/p9/server.go @@ -15,6 +15,8 @@ package p9 import ( + "io" + "runtime/debug" "sync" "sync/atomic" "syscall" @@ -27,6 +29,19 @@ import ( type Server struct { // attacher provides the attach function. attacher Attacher + + // pathTree is the full set of paths opened on this server. + // + // These may be across different connections, but rename operations + // must be serialized globally for safely. There is a single pathTree + // for the entire server, and not per connection. + pathTree pathNode + + // renameMu is a global lock protecting rename operations. With this + // lock, we can be certain that any given rename operation can safely + // acquire two path nodes in any order, as all other concurrent + // operations acquire at most a single node. + renameMu sync.RWMutex } // NewServer returns a new server. @@ -81,6 +96,9 @@ type connState struct { // fidRef wraps a node and tracks references. type fidRef struct { + // server is the associated server. + server *Server + // file is the associated File. file File @@ -97,13 +115,39 @@ type fidRef struct { // This is updated in handlers.go. opened bool - // walkable indicates this fidRef may be walked. - walkable bool + // mode is the fidRef's mode from the walk. Only the type bits are + // valid, the permissions may change. This is used to sanity check + // operations on this element, and prevent walks across + // non-directories. + mode FileMode // openFlags is the mode used in the open. // // This is updated in handlers.go. openFlags OpenFlags + + // pathNode is the current pathNode for this FID. + pathNode *pathNode + + // parent is the parent fidRef. We hold on to a parent reference to + // ensure that hooks, such as Renamed, can be executed safely by the + // server code. + // + // Note that parent cannot be changed without holding both the global + // rename lock and a writable lock on the associated pathNode for this + // fidRef. Holding either of these locks is sufficient to examine + // parent safely. + // + // The parent will be nil for root fidRefs, and non-nil otherwise. The + // method maybeParent can be used to return a cyclical reference, and + // isRoot should be used to check for root over looking at parent + // directly. + parent *fidRef + + // deleted indicates that the backing file has been deleted. We stop + // many operations at the API level if they are incompatible with a + // file that has already been unlinked. + deleted uint32 } // OpenFlags returns the flags the file was opened with and true iff the fid was opened previously. @@ -113,13 +157,146 @@ func (f *fidRef) OpenFlags() (OpenFlags, bool) { return f.openFlags, f.opened } +// IncRef increases the references on a fid. +func (f *fidRef) IncRef() { + atomic.AddInt64(&f.refs, 1) +} + // DecRef should be called when you're finished with a fid. func (f *fidRef) DecRef() { if atomic.AddInt64(&f.refs, -1) == 0 { f.file.Close() + + // Drop the parent reference. + // + // Since this fidRef is guaranteed to be non-discoverable when + // the references reach zero, we don't need to worry about + // clearing the parent. + if f.parent != nil { + // If we've been previously deleted, this removing this + // ref is a no-op. That's expected. + f.parent.pathNode.removeChild(f) + f.parent.DecRef() + } } } +// isDeleted returns true if this fidRef has been deleted. +func (f *fidRef) isDeleted() bool { + return atomic.LoadUint32(&f.deleted) != 0 +} + +// isRoot indicates whether this is a root fid. +func (f *fidRef) isRoot() bool { + return f.parent == nil +} + +// maybeParent returns a cyclic reference for roots, and the parent otherwise. +func (f *fidRef) maybeParent() *fidRef { + if f.parent != nil { + return f.parent + } + return f // Root has itself. +} + +// notifyDelete marks all fidRefs as deleted. +// +// Precondition: the write lock must be held on the given pathNode. +func notifyDelete(pn *pathNode) { + // Call on all local references. + pn.fidRefs.Range(func(key, _ interface{}) bool { + ref := key.(*fidRef) + atomic.StoreUint32(&ref.deleted, 1) + return true + }) + + // Call on all subtrees. + pn.children.Range(func(_, value interface{}) bool { + notifyDelete(value.(*pathNode)) + return true + }) +} + +// markChildDeleted marks all children below the given name as deleted. +// +// Precondition: this must be called via safelyWrite or safelyGlobal. +func (f *fidRef) markChildDeleted(name string) { + origPathNode := f.pathNode.removeWithName(name, func(ref *fidRef) { + atomic.StoreUint32(&ref.deleted, 1) + }) + + // Mark everything below as deleted. + notifyDelete(origPathNode) +} + +// notifyNameChange calls the relevant Renamed method on all nodes in the path, +// recursively. Note that this applies only for subtrees, as these +// notifications do not apply to the actual file whose name has changed. +// +// Precondition: the write lock must be held on the given pathNode. +func notifyNameChange(pn *pathNode) { + // Call on all local references. + pn.fidRefs.Range(func(key, value interface{}) bool { + ref := key.(*fidRef) + name := value.(string) + ref.file.Renamed(ref.parent.file, name) + return true + }) + + // Call on all subtrees. + pn.children.Range(func(_, value interface{}) bool { + notifyNameChange(value.(*pathNode)) + return true + }) +} + +// renameChildTo renames the given child to the target. +// +// Precondition: this must be called via safelyGlobal. +func (f *fidRef) renameChildTo(oldName string, target *fidRef, newName string) { + target.markChildDeleted(newName) + origPathNode := f.pathNode.removeWithName(oldName, func(ref *fidRef) { + ref.parent.DecRef() // Drop original reference. + ref.parent = target // Change parent. + ref.parent.IncRef() // Acquire new one. + target.pathNode.addChild(ref, newName) + ref.file.Renamed(target.file, newName) + }) + + // Replace the previous (now deleted) path node. + f.pathNode.children.Store(newName, origPathNode) + + // Call Renamed on everything above. + notifyNameChange(origPathNode) +} + +// safelyRead executes the given operation with the local path node locked. +// This implies that paths will not change during the operation. +func (f *fidRef) safelyRead(fn func() error) (err error) { + f.server.renameMu.RLock() + defer f.server.renameMu.RUnlock() + f.pathNode.mu.RLock() + defer f.pathNode.mu.RUnlock() + return fn() +} + +// safelyWrite executes the given operation with the local path node locked in +// a writable fashion. This implies some paths may change. +func (f *fidRef) safelyWrite(fn func() error) (err error) { + f.server.renameMu.RLock() + defer f.server.renameMu.RUnlock() + f.pathNode.mu.Lock() + defer f.pathNode.mu.Unlock() + return fn() +} + +// safelyGlobal executes the given operation with the global path lock held. +func (f *fidRef) safelyGlobal(fn func() error) (err error) { + f.server.renameMu.Lock() + defer f.server.renameMu.Unlock() + return fn() +} + // LookupFID finds the given FID. // // You should call fid.DecRef when you are finished using the fid. @@ -128,7 +305,7 @@ func (cs *connState) LookupFID(fid FID) (*fidRef, bool) { defer cs.fidMu.Unlock() fidRef, ok := cs.fids[fid] if ok { - atomic.AddInt64(&fidRef.refs, 1) + fidRef.IncRef() return fidRef, true } return nil, false @@ -145,7 +322,7 @@ func (cs *connState) InsertFID(fid FID, newRef *fidRef) { if ok { defer origRef.DecRef() } - atomic.AddInt64(&newRef.refs, 1) + newRef.IncRef() cs.fids[fid] = newRef } @@ -229,10 +406,9 @@ func (cs *connState) handleRequest() { cs.recvDone <- nil // Deal with other errors. - if err != nil { + if err != nil && err != io.EOF { // If it's not a connection error, but some other protocol error, // we can send a response immediately. - log.Debugf("err [%05d] %v", tag, err) cs.sendMu.Lock() err := send(cs.conn, tag, newErr(err)) cs.sendMu.Unlock() @@ -243,12 +419,38 @@ func (cs *connState) handleRequest() { // Try to start the tag. if !cs.StartTag(tag) { // Nothing we can do at this point; client is bogus. + log.Debugf("no valid tag [%05d]", tag) cs.sendDone <- ErrNoValidMessage return } // Handle the message. - var r message + var r message // r is the response. + defer func() { + if r == nil { + // Don't allow a panic to propagate. + recover() + + // Include a useful log message. + log.Warningf("panic in handler: %s", debug.Stack()) + + // Wrap in an EFAULT error; we don't really have a + // better way to describe this kind of error. It will + // usually manifest as a result of the test framework. + r = newErr(syscall.EFAULT) + } + + // Clear the tag before sending. That's because as soon as this + // hits the wire, the client can legally send another message + // with the same tag. + cs.ClearTag(tag) + + // Send back the result. + cs.sendMu.Lock() + err = send(cs.conn, tag, r) + cs.sendMu.Unlock() + cs.sendDone <- err + }() if handler, ok := m.(handler); ok { // Call the message handler. r = handler.handle(cs) @@ -256,18 +458,6 @@ func (cs *connState) handleRequest() { // Produce an ENOSYS error. r = newErr(syscall.ENOSYS) } - - // Clear the tag before sending. That's because as soon - // as this hits the wire, the client can legally send - // another message with the same tag. - cs.ClearTag(tag) - - // Send back the result. - cs.sendMu.Lock() - err = send(cs.conn, tag, r) - cs.sendMu.Unlock() - cs.sendDone <- err - return } func (cs *connState) handleRequests() { diff --git a/pkg/p9/transport.go b/pkg/p9/transport.go index 97396806c..bafb377de 100644 --- a/pkg/p9/transport.go +++ b/pkg/p9/transport.go @@ -167,7 +167,7 @@ func recv(s *unet.Socket, msize uint32, lookup lookupTagAndType) (Tag, message, r.EnableFDs(1) n, err := r.ReadVec([][]byte{hdr[:]}) - if err != nil { + if err != nil && (n == 0 || err != io.EOF) { r.CloseFDs() return NoTag, nil, ErrSocket{err} } @@ -189,10 +189,8 @@ func recv(s *unet.Socket, msize uint32, lookup lookupTagAndType) (Tag, message, // Continuing reading for a short header. for n < int(headerLength) { cur, err := r.ReadVec([][]byte{hdr[n:]}) - if err != nil { + if err != nil && (cur == 0 || err != io.EOF) { return NoTag, nil, ErrSocket{err} - } else if cur == 0 { - return NoTag, nil, ErrSocket{io.EOF} } n += cur } @@ -296,10 +294,8 @@ func recv(s *unet.Socket, msize uint32, lookup lookupTagAndType) (Tag, message, r := s.Reader(true) for n := 0; n < int(remaining); { cur, err := r.ReadVec(vecs) - if err != nil { + if err != nil && (cur == 0 || err != io.EOF) { return NoTag, nil, ErrSocket{err} - } else if cur == 0 { - return NoTag, nil, ErrSocket{io.EOF} } n += cur diff --git a/pkg/rand/BUILD b/pkg/rand/BUILD index 97b9ba3ff..0c9efc709 100644 --- a/pkg/rand/BUILD +++ b/pkg/rand/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "rand", srcs = [ diff --git a/pkg/seccomp/BUILD b/pkg/seccomp/BUILD index 1975d17a6..657f923ed 100644 --- a/pkg/seccomp/BUILD +++ b/pkg/seccomp/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_embed_data") +package(licenses = ["notice"]) # Apache 2.0 + go_binary( name = "victim", testonly = 1, diff --git a/pkg/secio/BUILD b/pkg/secio/BUILD index 0ed38c64a..29f751725 100644 --- a/pkg/secio/BUILD +++ b/pkg/secio/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "secio", srcs = [ diff --git a/pkg/sentry/arch/BUILD b/pkg/sentry/arch/BUILD index 314b3e962..9bf04360a 100644 --- a/pkg/sentry/arch/BUILD +++ b/pkg/sentry/arch/BUILD @@ -1,6 +1,7 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") + package(licenses = ["notice"]) # Apache 2.0 -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("//tools/go_stateify:defs.bzl", "go_library") go_library( diff --git a/pkg/sentry/context/BUILD b/pkg/sentry/context/BUILD index 2a7a6df23..02d24defd 100644 --- a/pkg/sentry/context/BUILD +++ b/pkg/sentry/context/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "context", srcs = ["context.go"], diff --git a/pkg/sentry/control/BUILD b/pkg/sentry/control/BUILD index fbdde0721..c3b682d6f 100644 --- a/pkg/sentry/control/BUILD +++ b/pkg/sentry/control/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "control", srcs = [ diff --git a/pkg/sentry/device/BUILD b/pkg/sentry/device/BUILD index 69c99b0b3..bebdb2939 100644 --- a/pkg/sentry/device/BUILD +++ b/pkg/sentry/device/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "device", srcs = ["device.go"], diff --git a/pkg/sentry/fs/anon/BUILD b/pkg/sentry/fs/anon/BUILD index ff4ab850a..4bd912e95 100644 --- a/pkg/sentry/fs/anon/BUILD +++ b/pkg/sentry/fs/anon/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "anon", srcs = [ diff --git a/pkg/sentry/fs/gofer/BUILD b/pkg/sentry/fs/gofer/BUILD index cef01829a..c9e531e40 100644 --- a/pkg/sentry/fs/gofer/BUILD +++ b/pkg/sentry/fs/gofer/BUILD @@ -56,14 +56,10 @@ go_test( srcs = ["gofer_test.go"], embed = [":gofer"], deps = [ - "//pkg/log", "//pkg/p9", "//pkg/p9/p9test", "//pkg/sentry/context", "//pkg/sentry/context/contexttest", "//pkg/sentry/fs", - "//pkg/sentry/kernel/time", - "//pkg/sentry/usermem", - "//pkg/unet", ], ) diff --git a/pkg/sentry/fs/gofer/context_file.go b/pkg/sentry/fs/gofer/context_file.go index a0265c2aa..455953237 100644 --- a/pkg/sentry/fs/gofer/context_file.go +++ b/pkg/sentry/fs/gofer/context_file.go @@ -58,13 +58,6 @@ func (c *contextFile) setAttr(ctx context.Context, valid p9.SetAttrMask, attr p9 return c.file.SetAttr(valid, attr) } -func (c *contextFile) remove(ctx context.Context) error { - ctx.UninterruptibleSleepStart(false) - defer ctx.UninterruptibleSleepFinish(false) - - return c.file.Remove() -} - func (c *contextFile) rename(ctx context.Context, directory contextFile, name string) error { ctx.UninterruptibleSleepStart(false) defer ctx.UninterruptibleSleepFinish(false) diff --git a/pkg/sentry/fs/gofer/gofer_test.go b/pkg/sentry/fs/gofer/gofer_test.go index 3190d1e18..b450778ca 100644 --- a/pkg/sentry/fs/gofer/gofer_test.go +++ b/pkg/sentry/fs/gofer/gofer_test.go @@ -16,110 +16,102 @@ package gofer import ( "fmt" - "io" "syscall" "testing" "time" - "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/pkg/p9" "gvisor.googlesource.com/gvisor/pkg/p9/p9test" "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" - ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" - "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" - "gvisor.googlesource.com/gvisor/pkg/unet" ) -// goodMockFile returns a file that can be Walk'ed to and created. -func goodMockFile(mode p9.FileMode, size uint64) *p9test.FileMock { - return &p9test.FileMock{ - GetAttrMock: p9test.GetAttrMock{ - Attr: p9.Attr{Mode: mode, Size: size, RDev: 0}, - Valid: p9.AttrMaskAll(), - }, - } -} - -func newClosedSocket() (*unet.Socket, error) { - fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) - if err != nil { - return nil, err - } - - s, err := unet.NewSocket(fd) - if err != nil { - syscall.Close(fd) - return nil, err - } - - return s, s.Close() -} - -// root returns a p9 file mock and an fs.InodeOperations created from that file. Any -// functions performed on fs.InodeOperations will use the p9 file mock. -func root(ctx context.Context, cp cachePolicy, mode p9.FileMode, size uint64) (*p9test.FileMock, *fs.Inode, error) { - sock, err := newClosedSocket() - if err != nil { - return nil, nil, err - } - - // Construct a dummy session that we can destruct. - s := &session{ - conn: sock, - mounter: fs.RootOwner, - cachePolicy: cp, - client: &p9.Client{}, - } - - rootFile := goodMockFile(mode, size) - sattr, rootInodeOperations := newInodeOperations(ctx, s, contextFile{file: rootFile}, p9.QID{}, rootFile.GetAttrMock.Valid, rootFile.GetAttrMock.Attr, false /* socket */) - m := fs.NewMountSource(s, &filesystem{}, fs.MountSourceFlags{}) - return rootFile, fs.NewInode(rootInodeOperations, m, sattr), nil +// rootTest runs a test with a p9 mock and an fs.InodeOperations created from +// the attached root directory. The root file will be closed and client +// disconnected, but additional files must be closed manually. +func rootTest(t *testing.T, name string, cp cachePolicy, fn func(context.Context, *p9test.Harness, *p9test.Mock, *fs.Inode)) { + t.Run(name, func(t *testing.T) { + h, c := p9test.NewHarness(t) + defer h.Finish() + + // Create a new root. Note that we pass an empty, but non-nil + // map here. This allows tests to extend the root children + // dynamically. + root := h.NewDirectory(map[string]p9test.Generator{})(nil) + + // Return this as the root. + h.Attacher.EXPECT().Attach().Return(root, nil).Times(1) + + // ... and open via the client. + rootFile, err := c.Attach("/") + if err != nil { + t.Fatalf("unable to attach: %v", err) + } + defer rootFile.Close() + + // Wrap an a session. + s := &session{ + mounter: fs.RootOwner, + cachePolicy: cp, + client: c, + } + + // ... and an INode, with only the mode being explicitly valid for now. + ctx := contexttest.Context(t) + sattr, rootInodeOperations := newInodeOperations(ctx, s, contextFile{ + file: rootFile, + }, root.QID, p9.AttrMaskAll(), root.Attr, false /* socket */) + m := fs.NewMountSource(s, &filesystem{}, fs.MountSourceFlags{}) + rootInode := fs.NewInode(rootInodeOperations, m, sattr) + + // Ensure that the cache is fully invalidated, so that any + // close actions actually take place before the full harness is + // torn down. + defer m.FlushDirentRefs() + + // Execute the test. + fn(ctx, h, root, rootInode) + }) } func TestLookup(t *testing.T) { - // Test parameters. type lookupTest struct { // Name of the test. name string - // Function input parameters. - fileName string - // Expected return value. want error } tests := []lookupTest{ { - name: "mock Walk passes (function succeeds)", - fileName: "ppp", - want: nil, + name: "mock Walk passes (function succeeds)", + want: nil, }, { - name: "mock Walk fails (function fails)", - fileName: "ppp", - want: syscall.ENOENT, + name: "mock Walk fails (function fails)", + want: syscall.ENOENT, }, } - ctx := contexttest.Context(t) + const file = "file" // The walked target file. + for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Set up mock. - rootFile, rootInode, err := root(ctx, cacheNone, p9.PermissionsMask, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) + rootTest(t, test.name, cacheNone, func(ctx context.Context, h *p9test.Harness, rootFile *p9test.Mock, rootInode *fs.Inode) { + // Setup the appropriate result. + rootFile.WalkCallback = func() error { + return test.want + } + if test.want == nil { + // Set the contents of the root. We expect a + // normal file generator for ppp above. This is + // overriden by setting WalkErr in the mock. + rootFile.AddChild(file, h.NewFile()) } - - rootFile.WalkGetAttrMock.QIDs = []p9.QID{{}} - rootFile.WalkGetAttrMock.Err = test.want - rootFile.WalkGetAttrMock.File = goodMockFile(p9.PermissionsMask, 0) // Call function. - dirent, err := rootInode.Lookup(ctx, test.fileName) + dirent, err := rootInode.Lookup(ctx, file) // Unwrap the InodeOperations. var newInodeOperations fs.InodeOperations @@ -138,19 +130,12 @@ func TestLookup(t *testing.T) { if err == nil && newInodeOperations == nil { t.Errorf("Lookup got non-nil err and non-nil node, wanted at least one non-nil") } - - // Check mock parameters. - if !rootFile.WalkGetAttrMock.Called { - t.Errorf("GetAttr not called; error: %v", err) - } else if rootFile.WalkGetAttrMock.Names[0] != test.fileName { - t.Errorf("file name not set") - } }) } } func TestRevalidation(t *testing.T) { - tests := []struct { + type revalidationTest struct { cachePolicy cachePolicy // Whether dirent should be reloaded before any modifications. @@ -167,7 +152,9 @@ func TestRevalidation(t *testing.T) { // Whether dirent should be reloaded after the remote has // removed the file. postRemovalWantReload bool - }{ + } + + tests := []revalidationTest{ { // Policy cacheNone causes Revalidate to always return // true. @@ -208,67 +195,83 @@ func TestRevalidation(t *testing.T) { }, } - ctx := contexttest.Context(t) + const file = "file" // The file walked below. + for _, test := range tests { name := fmt.Sprintf("cachepolicy=%s", test.cachePolicy) - t.Run(name, func(t *testing.T) { - // Set up mock. - rootFile, rootInode, err := root(ctx, test.cachePolicy, p9.ModeDirectory|p9.PermissionsMask, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - + rootTest(t, name, test.cachePolicy, func(ctx context.Context, h *p9test.Harness, rootFile *p9test.Mock, rootInode *fs.Inode) { + // Wrap in a dirent object. rootDir := fs.NewDirent(rootInode, "root") - // Create a mock file that we will walk to from the root. - const ( - name = "foo" - mode = p9.PermissionsMask - ) - file := goodMockFile(mode, 0) - file.GetAttrMock.Valid = p9.AttrMaskAll() - - // Tell the root mock how to walk to this file. - rootFile.WalkGetAttrMock.QIDs = []p9.QID{{}} - rootFile.WalkGetAttrMock.File = file - rootFile.WalkGetAttrMock.Attr = file.GetAttrMock.Attr - rootFile.WalkGetAttrMock.Valid = file.GetAttrMock.Valid + // Create a mock file a child of the root. We save when + // this is generated, so that when the time changed, we + // can update the original entry. + var origMocks []*p9test.Mock + rootFile.AddChild(file, func(parent *p9test.Mock) *p9test.Mock { + // Regular a regular file that has a consistent + // path number. This might be used by + // validation so we don't change it. + m := h.NewMock(parent, 0, p9.Attr{ + Mode: p9.ModeRegular, + }) + origMocks = append(origMocks, m) + return m + }) // Do the walk. - dirent, err := rootDir.Walk(ctx, rootDir, name) + dirent, err := rootDir.Walk(ctx, rootDir, file) if err != nil { - t.Fatalf("Lookup(%q) failed: %v", name, err) + t.Fatalf("Lookup failed: %v", err) } - // Walk again. Depending on the cache policy, we may get a new - // dirent. - newDirent, err := rootDir.Walk(ctx, rootDir, name) + // We must release the dirent, of the test will fail + // with a reference leak. This is tracked by p9test. + defer dirent.DecRef() + + // Walk again. Depending on the cache policy, we may + // get a new dirent. + newDirent, err := rootDir.Walk(ctx, rootDir, file) if err != nil { - t.Fatalf("Lookup(%q) failed: %v", name, err) + t.Fatalf("Lookup failed: %v", err) } if test.preModificationWantReload && dirent == newDirent { - t.Errorf("Lookup(%q) with cachePolicy=%s got old dirent %v, wanted a new dirent", name, test.cachePolicy, dirent) + t.Errorf("Lookup with cachePolicy=%s got old dirent %+v, wanted a new dirent", test.cachePolicy, dirent) } if !test.preModificationWantReload && dirent != newDirent { - t.Errorf("Lookup(%q) with cachePolicy=%s got new dirent %v, wanted old dirent %v", name, test.cachePolicy, newDirent, dirent) + t.Errorf("Lookup with cachePolicy=%s got new dirent %+v, wanted old dirent %+v", test.cachePolicy, newDirent, dirent) } + newDirent.DecRef() // See above. - // Modify the underlying mocked file's modification time. + // Modify the underlying mocked file's modification + // time for the next walk that occurs. nowSeconds := time.Now().Unix() - rootFile.WalkGetAttrMock.Attr.MTimeSeconds = uint64(nowSeconds) - file.GetAttrMock.Attr.MTimeSeconds = uint64(nowSeconds) + rootFile.AddChild(file, func(parent *p9test.Mock) *p9test.Mock { + // Ensure that the path is the same as above, + // but we change only the modification time of + // the file. + return h.NewMock(parent, 0, p9.Attr{ + Mode: p9.ModeRegular, + MTimeSeconds: uint64(nowSeconds), + }) + }) + + // We also modify the original time, so that GetAttr + // behaves as expected for the caching case. + for _, m := range origMocks { + m.Attr.MTimeSeconds = uint64(nowSeconds) + } - // Walk again. Depending on the cache policy, we may get a new - // dirent. - newDirent, err = rootDir.Walk(ctx, rootDir, name) + // Walk again. Depending on the cache policy, we may + // get a new dirent. + newDirent, err = rootDir.Walk(ctx, rootDir, file) if err != nil { - t.Fatalf("Lookup(%q) failed: %v", name, err) + t.Fatalf("Lookup failed: %v", err) } if test.postModificationWantReload && dirent == newDirent { - t.Errorf("Lookup(%q) with cachePolicy=%s got old dirent %v, wanted a new dirent", name, test.cachePolicy, dirent) + t.Errorf("Lookup with cachePolicy=%s got old dirent, wanted a new dirent", test.cachePolicy) } if !test.postModificationWantReload && dirent != newDirent { - t.Errorf("Lookup(%q) with cachePolicy=%s got new dirent %v, wanted old dirent %v", name, test.cachePolicy, newDirent, dirent) + t.Errorf("Lookup with cachePolicy=%s got new dirent, wanted old dirent", test.cachePolicy) } uattrs, err := newDirent.Inode.UnstableAttr(ctx) if err != nil { @@ -276,660 +279,25 @@ func TestRevalidation(t *testing.T) { } gotModTimeSeconds := uattrs.ModificationTime.Seconds() if test.postModificationWantUpdatedAttrs && gotModTimeSeconds != nowSeconds { - t.Fatalf("Lookup(%q) with cachePolicy=%s got new modification time %v, wanted %v", name, test.cachePolicy, gotModTimeSeconds, nowSeconds) + t.Fatalf("Lookup with cachePolicy=%s got new modification time %v, wanted %v", test.cachePolicy, gotModTimeSeconds, nowSeconds) } + newDirent.DecRef() // See above. - // Make WalkGetAttr return ENOENT. This simulates - // removing the file from the remote fs. - rootFile.WalkGetAttrMock = p9test.WalkGetAttrMock{ - Err: syscall.ENOENT, - } + // Remove the file from the remote fs, subsequent walks + // should now fail to find anything. + rootFile.RemoveChild(file) // Walk again. Depending on the cache policy, we may // get ENOENT. - newDirent, err = rootDir.Walk(ctx, rootDir, name) + newDirent, err = rootDir.Walk(ctx, rootDir, file) if test.postRemovalWantReload && err == nil { - t.Errorf("Lookup(%q) with cachePolicy=%s got nil error, wanted ENOENT", name, test.cachePolicy) + t.Errorf("Lookup with cachePolicy=%s got nil error, wanted ENOENT", test.cachePolicy) } if !test.postRemovalWantReload && (err != nil || dirent != newDirent) { - t.Errorf("Lookup(%q) with cachePolicy=%s got new dirent %v and error %v, wanted old dirent %v and nil error", name, test.cachePolicy, newDirent, err, dirent) - } - }) - } -} - -func TestSetTimestamps(t *testing.T) { - // Test parameters. - type setTimestampsTest struct { - // Name of the test. - name string - - // Function input parameters. - ts fs.TimeSpec - } - - ctx := contexttest.Context(t) - now := ktime.NowFromContext(ctx) - tests := []setTimestampsTest{ - { - name: "mock SetAttr passes (function succeeds)", - ts: fs.TimeSpec{ - ATime: now, - MTime: now, - }, - }, - { - name: "mock SetAttr passes, times are 0 (function succeeds)", - ts: fs.TimeSpec{}, - }, - { - name: "mock SetAttr passes, times are 0 and not system time (function succeeds)", - ts: fs.TimeSpec{ - ATimeSetSystemTime: false, - MTimeSetSystemTime: false, - }, - }, - { - name: "mock SetAttr passes, times are set to system time (function succeeds)", - ts: fs.TimeSpec{ - ATimeSetSystemTime: true, - MTimeSetSystemTime: true, - }, - }, - { - name: "mock SetAttr passes, times are omitted (function succeeds)", - ts: fs.TimeSpec{ - ATimeOmit: true, - MTimeOmit: true, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Set up mock. - rootFile, rootInode, err := root(ctx, cacheNone, p9.PermissionsMask, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - - // Call function. - err = rootInode.SetTimestamps(ctx, nil /* Dirent */, test.ts) - - // Check return values. - if err != nil { - t.Errorf("SetTimestamps failed: got error %v, want nil", err) - } - - // Check mock parameters. - if !(test.ts.ATimeOmit && test.ts.MTimeOmit) && !rootFile.SetAttrMock.Called { - t.Errorf("TestSetTimestamps failed: SetAttr not called") - return - } - - // Check what was passed to the mock function. - attr := rootFile.SetAttrMock.Attr - atimeGiven := ktime.FromUnix(int64(attr.ATimeSeconds), int64(attr.ATimeNanoSeconds)) - if test.ts.ATimeOmit { - if rootFile.SetAttrMock.Valid.ATime { - t.Errorf("ATime got set true in mask, wanted false") - } - } else { - if got, want := rootFile.SetAttrMock.Valid.ATimeNotSystemTime, !test.ts.ATimeSetSystemTime; got != want { - t.Errorf("got ATimeNotSystemTime %v, want %v", got, want) - } - if !test.ts.ATimeSetSystemTime && !test.ts.ATime.Equal(atimeGiven) { - t.Errorf("ATime got %v, want %v", atimeGiven, test.ts.ATime) - } - } - - mtimeGiven := ktime.FromUnix(int64(attr.MTimeSeconds), int64(attr.MTimeNanoSeconds)) - if test.ts.MTimeOmit { - if rootFile.SetAttrMock.Valid.MTime { - t.Errorf("MTime got set true in mask, wanted false") - } - } else { - if got, want := rootFile.SetAttrMock.Valid.MTimeNotSystemTime, !test.ts.MTimeSetSystemTime; got != want { - t.Errorf("got MTimeNotSystemTime %v, want %v", got, want) - } - if !test.ts.MTimeSetSystemTime && !test.ts.MTime.Equal(mtimeGiven) { - t.Errorf("MTime got %v, want %v", mtimeGiven, test.ts.MTime) - } - } - }) - } -} - -func TestSetPermissions(t *testing.T) { - // Test parameters. - type setPermissionsTest struct { - // Name of the test. - name string - - // SetPermissions input parameters. - perms fs.FilePermissions - - // Error that SetAttr mock should return. - setAttrErr error - - // Expected return value. - want bool - } - - tests := []setPermissionsTest{ - { - name: "SetAttr mock succeeds (function succeeds)", - perms: fs.FilePermissions{User: fs.PermMask{Read: true, Write: true, Execute: true}}, - want: true, - setAttrErr: nil, - }, - { - name: "SetAttr mock fails (function fails)", - perms: fs.FilePermissions{User: fs.PermMask{Read: true, Write: true}}, - want: false, - setAttrErr: syscall.ENOENT, - }, - } - - ctx := contexttest.Context(t) - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Set up mock. - rootFile, rootInode, err := root(ctx, cacheNone, 0, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - rootFile.SetAttrMock.Err = test.setAttrErr - - ok := rootInode.SetPermissions(ctx, nil /* Dirent */, test.perms) - - // Check return value. - if ok != test.want { - t.Errorf("SetPermissions got %v, want %v", ok, test.want) - } - - // Check mock parameters. - pattr := rootFile.SetAttrMock.Attr - if !rootFile.SetAttrMock.Called { - t.Errorf("SetAttr not called") - return - } - if !rootFile.SetAttrMock.Valid.Permissions { - t.Errorf("SetAttr did not get right request (got false, expected SetAttrMask.Permissions true)") - } - if got := fs.FilePermsFromP9(pattr.Permissions); got != test.perms { - t.Errorf("SetAttr did not get right permissions -- got %v, want %v", got, test.perms) - } - }) - } -} - -func TestClose(t *testing.T) { - ctx := contexttest.Context(t) - // Set up mock. - rootFile, rootInode, err := root(ctx, cacheNone, p9.PermissionsMask, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - - // Call function. - rootInode.InodeOperations.Release(ctx) - - // Check mock parameters. - if !rootFile.CloseMock.Called { - t.Errorf("TestClose failed: Close not called") - } -} - -func TestRename(t *testing.T) { - // Test parameters. - type renameTest struct { - // Name of the test. - name string - - // Input parameters. - newParent *fs.Inode - newName string - - // Rename mock parameters. - renameErr error - renameCalled bool - - // Error want to return given the parameters. (Same as what - // we expect and tell rename to return.) - want error - } - ctx := contexttest.Context(t) - rootFile, rootInode, err := root(ctx, cacheNone, p9.PermissionsMask, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - - tests := []renameTest{ - { - name: "mock Rename succeeds (function succeeds)", - newParent: rootInode, - newName: "foo2", - want: nil, - renameErr: nil, - renameCalled: true, - }, - { - name: "mock Rename fails (function fails)", - newParent: rootInode, - newName: "foo2", - want: syscall.ENOENT, - renameErr: syscall.ENOENT, - renameCalled: true, - }, - { - name: "newParent is not inodeOperations but should be (function fails)", - newParent: fs.NewMockInode(ctx, fs.NewMockMountSource(nil), fs.StableAttr{Type: fs.Directory}), - newName: "foo2", - want: syscall.EXDEV, - renameErr: nil, - renameCalled: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - mockFile := goodMockFile(p9.PermissionsMask, 0) - rootFile.WalkGetAttrMock.QIDs = []p9.QID{{}} - rootFile.WalkGetAttrMock.File = mockFile - - dirent, err := rootInode.Lookup(ctx, "foo") - if err != nil { - t.Fatalf("root.Walk failed: %v", err) - } - mockFile.RenameMock.Err = test.renameErr - mockFile.RenameMock.Called = false - - // Use a dummy oldParent to acquire write access to that directory. - oldParent := &inodeOperations{ - readdirCache: fs.NewSortedDentryMap(nil), - } - oldInode := fs.NewInode(oldParent, fs.NewMockMountSource(nil), fs.StableAttr{Type: fs.Directory}) - - // Call function. - err = dirent.Inode.InodeOperations.Rename(ctx, oldInode, "", test.newParent, test.newName) - - // Check return value. - if err != test.want { - t.Errorf("Rename got %v, want %v", err, test.want) - } - - // Check mock parameters. - if got, want := mockFile.RenameMock.Called, test.renameCalled; got != want { - t.Errorf("renameCalled got %v want %v", got, want) - } - }) - } -} - -// This file is read from in TestPreadv. -type readAtFileFake struct { - p9test.FileMock - - // Parameters for faking ReadAt. - FileLength int - Err error - ChunkSize int - Called bool - LengthRead int -} - -func (r *readAtFileFake) ReadAt(p []byte, offset uint64) (int, error) { - r.Called = true - log.Warningf("ReadAt fake: length read so far = %d, len(p) = %d, offset = %d", r.LengthRead, len(p), offset) - if int(offset) != r.LengthRead { - return 0, fmt.Errorf("offset got %d; expected %d", offset, r.LengthRead) - } - - if r.Err != nil { - return 0, r.Err - } - - if r.LengthRead >= r.FileLength { - return 0, io.EOF - } - - // Read at most ChunkSize and read at most what's left in the file. - toBeRead := len(p) - if r.LengthRead+toBeRead >= r.FileLength { - toBeRead = r.FileLength - int(offset) - } - if toBeRead > r.ChunkSize { - toBeRead = r.ChunkSize - } - - r.LengthRead += toBeRead - if r.LengthRead == r.FileLength { - return toBeRead, io.EOF - } - return toBeRead, nil -} - -func TestPreadv(t *testing.T) { - // Test parameters. - type preadvTest struct { - // Name of the test. - name string - - // Mock parameters - mode p9.FileMode - - // Buffer to read into. - buffer [512]byte - sliceSize int - - // How much readAt returns at a time. - chunkSize int - - // Whether or not we expect ReadAt to be called. - readAtCalled bool - readAtErr error - - // Expected return values. - want error - } - - tests := []preadvTest{ - { - name: "fake ReadAt succeeds, 512 bytes requested, 512 byte chunks (function succeeds)", - want: nil, - readAtErr: nil, - mode: p9.PermissionsMask, - readAtCalled: true, - sliceSize: 512, - chunkSize: 512, - }, - { - name: "fake ReadAt succeeds, 512 bytes requested, 200 byte chunks (function succeeds)", - want: nil, - readAtErr: nil, - mode: p9.PermissionsMask, - readAtCalled: true, - sliceSize: 512, - chunkSize: 200, - }, - { - name: "fake ReadAt succeeds, 0 bytes requested (function succeeds)", - want: nil, - readAtErr: nil, - mode: p9.PermissionsMask, - readAtCalled: false, - sliceSize: 0, - chunkSize: 100, - }, - { - name: "fake ReadAt returns 0 bytes and EOF (function fails)", - want: io.EOF, - readAtErr: io.EOF, - mode: p9.PermissionsMask, - readAtCalled: true, - sliceSize: 512, - chunkSize: 512, - }, - } - - ctx := contexttest.Context(t) - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Set up mock. - rootFile, rootInode, err := root(ctx, cacheNone, test.mode, 1024) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - - // Set up the read buffer. - dst := usermem.BytesIOSequence(test.buffer[:test.sliceSize]) - - // This file will be read from. - openFile := &readAtFileFake{ - Err: test.readAtErr, - FileLength: test.sliceSize, - ChunkSize: test.chunkSize, - } - rootFile.WalkGetAttrMock.File = openFile - rootFile.WalkGetAttrMock.Attr.Mode = test.mode - rootFile.WalkGetAttrMock.Valid.Mode = true - - f := NewFile( - ctx, - fs.NewDirent(rootInode, ""), - "", - fs.FileFlags{Read: true}, - rootInode.InodeOperations.(*inodeOperations), - &handles{File: contextFile{file: openFile}}, - ) - - // Call function. - _, err = f.Preadv(ctx, dst, 0) - - // Check return value. - if err != test.want { - t.Errorf("Preadv got %v, want %v", err, test.want) - } - - // Check mock parameters. - if test.readAtCalled != openFile.Called { - t.Errorf("ReadAt called: %v, but expected opposite", openFile.Called) - } - }) - } -} - -func TestReadlink(t *testing.T) { - // Test parameters. - type readlinkTest struct { - // Name of the test. - name string - - // Mock parameters - mode p9.FileMode - - // Whether or not we expect ReadAt to be called and what error - // it shall return. - readlinkCalled bool - readlinkErr error - - // Expected return values. - want error - } - - tests := []readlinkTest{ - { - name: "file is not symlink (function fails)", - want: syscall.ENOLINK, - mode: p9.PermissionsMask, - readlinkCalled: false, - readlinkErr: nil, - }, - { - name: "mock Readlink succeeds (function succeeds)", - want: nil, - mode: p9.PermissionsMask | p9.ModeSymlink, - readlinkCalled: true, - readlinkErr: nil, - }, - { - name: "mock Readlink fails (function fails)", - want: syscall.ENOENT, - mode: p9.PermissionsMask | p9.ModeSymlink, - readlinkCalled: true, - readlinkErr: syscall.ENOENT, - }, - } - - ctx := contexttest.Context(t) - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Set up mock. - rootFile, rootInode, err := root(ctx, cacheNone, test.mode, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - - openFile := goodMockFile(test.mode, 0) - rootFile.WalkMock.File = openFile - rootFile.ReadlinkMock.Err = test.readlinkErr - - // Call function. - _, err = rootInode.Readlink(ctx) - - // Check return value. - if err != test.want { - t.Errorf("Readlink got %v, want %v", err, test.want) - } - - // Check mock parameters. - if test.readlinkCalled && !rootFile.ReadlinkMock.Called { - t.Errorf("Readlink not called") - } - }) - } -} - -// This file is write from in TestPwritev. -type writeAtFileFake struct { - p9test.FileMock - - // Parameters for faking WriteAt. - Err error - ChunkSize int - Called bool - LengthWritten int -} - -func (r *writeAtFileFake) WriteAt(p []byte, offset uint64) (int, error) { - r.Called = true - log.Warningf("WriteAt fake: length written so far = %d, len(p) = %d, offset = %d", r.LengthWritten, len(p), offset) - if int(offset) != r.LengthWritten { - return 0, fmt.Errorf("offset got %d; want %d", offset, r.LengthWritten) - } - - if r.Err != nil { - return 0, r.Err - } - - // Write at most ChunkSize. - toBeWritten := len(p) - if toBeWritten > r.ChunkSize { - toBeWritten = r.ChunkSize - } - r.LengthWritten += toBeWritten - return toBeWritten, nil -} - -func TestPwritev(t *testing.T) { - // Test parameters. - type pwritevTest struct { - // Name of the test. - name string - - // Mock parameters - mode p9.FileMode - - allowWrite bool - - // Buffer to write into. - buffer [512]byte - sliceSize int - chunkSize int - - // Whether or not we expect writeAt to be called. - writeAtCalled bool - writeAtErr error - - // Expected return values. - want error - } - - tests := []pwritevTest{ - { - name: "fake writeAt succeeds, one chunk (function succeeds)", - want: nil, - writeAtErr: nil, - mode: p9.PermissionsMask, - allowWrite: true, - writeAtCalled: true, - sliceSize: 512, - chunkSize: 512, - }, - { - name: "fake writeAt fails, short write (function fails)", - want: io.ErrShortWrite, - writeAtErr: nil, - mode: p9.PermissionsMask, - allowWrite: true, - writeAtCalled: true, - sliceSize: 512, - chunkSize: 200, - }, - { - name: "fake writeAt succeeds, len 0 (function succeeds)", - want: nil, - writeAtErr: nil, - mode: p9.PermissionsMask, - allowWrite: true, - writeAtCalled: false, - sliceSize: 0, - chunkSize: 0, - }, - { - name: "writeAt can still write despite file permissions read only (function succeeds)", - want: nil, - writeAtErr: nil, - mode: p9.PermissionsMask, - allowWrite: false, - writeAtCalled: true, - sliceSize: 512, - chunkSize: 512, - }, - } - - ctx := contexttest.Context(t) - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Set up mock. - _, rootInode, err := root(ctx, cacheNone, test.mode, 0) - if err != nil { - t.Fatalf("error creating root: %v", err) - } - - src := usermem.BytesIOSequence(test.buffer[:test.sliceSize]) - - // This is the file that will be used for writing. - openFile := &writeAtFileFake{ - Err: test.writeAtErr, - ChunkSize: test.chunkSize, - } - - f := NewFile( - ctx, - fs.NewDirent(rootInode, ""), - "", - fs.FileFlags{Write: true}, - rootInode.InodeOperations.(*inodeOperations), - &handles{File: contextFile{file: openFile}}, - ) - - // Call function. - _, err = f.Pwritev(ctx, src, 0) - - // Check return value. - if err != test.want { - t.Errorf("Pwritev got %v, want %v", err, test.want) - } - - // Check mock parameters. - if test.writeAtCalled != openFile.Called { - t.Errorf("WriteAt called: %v, but expected opposite", openFile.Called) - return + t.Errorf("Lookup with cachePolicy=%s got new dirent and error %v, wanted old dirent and nil error", test.cachePolicy, err) } - if openFile.Called && test.writeAtErr != nil && openFile.LengthWritten != test.sliceSize { - t.Errorf("wrote %d bytes, expected %d bytes written", openFile.LengthWritten, test.sliceSize) + if err == nil { + newDirent.DecRef() // See above. } }) } diff --git a/pkg/sentry/fs/gofer/session.go b/pkg/sentry/fs/gofer/session.go index 7552216f3..f76a83cd9 100644 --- a/pkg/sentry/fs/gofer/session.go +++ b/pkg/sentry/fs/gofer/session.go @@ -91,10 +91,6 @@ func (e *endpointMaps) get(key device.MultiDeviceKey) transport.BoundEndpoint { type session struct { refs.AtomicRefCount - // conn is a unet.Socket that wraps the readFD/writeFD mount option, - // see fs/gofer/fs.go. - conn *unet.Socket `state:"nosave"` - // msize is the value of the msize mount option, see fs/gofer/fs.go. msize uint32 `state:"wait"` @@ -142,7 +138,7 @@ type session struct { // Destroy tears down the session. func (s *session) Destroy() { - s.conn.Close() + s.client.Close() } // Revalidate implements MountSource.Revalidate. @@ -235,7 +231,6 @@ func Root(ctx context.Context, dev string, filesystem fs.Filesystem, superBlockF // Construct the session. s := &session{ connID: dev, - conn: conn, msize: o.msize, version: o.version, cachePolicy: o.policy, @@ -252,7 +247,7 @@ func Root(ctx context.Context, dev string, filesystem fs.Filesystem, superBlockF m := fs.NewMountSource(s, filesystem, superBlockFlags) // Send the Tversion request. - s.client, err = p9.NewClient(s.conn, s.msize, s.version) + s.client, err = p9.NewClient(conn, s.msize, s.version) if err != nil { // Drop our reference on the session, it needs to be torn down. s.DecRef() diff --git a/pkg/sentry/fs/gofer/session_state.go b/pkg/sentry/fs/gofer/session_state.go index f657135fc..d9fd7a221 100644 --- a/pkg/sentry/fs/gofer/session_state.go +++ b/pkg/sentry/fs/gofer/session_state.go @@ -84,13 +84,13 @@ func (s *session) afterLoad() { } // Manually restore the connection. - s.conn, err = unet.NewSocket(opts.fd) + conn, err := unet.NewSocket(opts.fd) if err != nil { panic(fmt.Sprintf("failed to create Socket for FD %d: %v", opts.fd, err)) } // Manually restore the client. - s.client, err = p9.NewClient(s.conn, s.msize, s.version) + s.client, err = p9.NewClient(conn, s.msize, s.version) if err != nil { panic(fmt.Sprintf("failed to connect client to server: %v", err)) } diff --git a/pkg/sentry/fs/proc/device/BUILD b/pkg/sentry/fs/proc/device/BUILD index 34582f275..ff7dacf07 100644 --- a/pkg/sentry/fs/proc/device/BUILD +++ b/pkg/sentry/fs/proc/device/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "device", srcs = ["device.go"], diff --git a/pkg/sentry/hostcpu/BUILD b/pkg/sentry/hostcpu/BUILD index f362d15c8..33197cf14 100644 --- a/pkg/sentry/hostcpu/BUILD +++ b/pkg/sentry/hostcpu/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "hostcpu", srcs = [ diff --git a/pkg/sentry/kernel/kdefs/BUILD b/pkg/sentry/kernel/kdefs/BUILD index fe6fa2260..3f8fa206c 100644 --- a/pkg/sentry/kernel/kdefs/BUILD +++ b/pkg/sentry/kernel/kdefs/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "kdefs", srcs = ["kdefs.go"], diff --git a/pkg/sentry/kernel/memevent/BUILD b/pkg/sentry/kernel/memevent/BUILD index 66899910c..e903badd3 100644 --- a/pkg/sentry/kernel/memevent/BUILD +++ b/pkg/sentry/kernel/memevent/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "memevent", srcs = ["memory_events.go"], diff --git a/pkg/sentry/kernel/sched/BUILD b/pkg/sentry/kernel/sched/BUILD index 125792f39..52e226a39 100644 --- a/pkg/sentry/kernel/sched/BUILD +++ b/pkg/sentry/kernel/sched/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "sched", srcs = [ diff --git a/pkg/sentry/loader/BUILD b/pkg/sentry/loader/BUILD index 0beb4561b..83cad186a 100644 --- a/pkg/sentry/loader/BUILD +++ b/pkg/sentry/loader/BUILD @@ -1,6 +1,7 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_embed_data") + package(licenses = ["notice"]) # Apache 2.0 -load("@io_bazel_rules_go//go:def.bzl", "go_embed_data") load("//tools/go_stateify:defs.bzl", "go_library") go_embed_data( diff --git a/pkg/sentry/memutil/BUILD b/pkg/sentry/memutil/BUILD index 341b30b98..88738d65d 100644 --- a/pkg/sentry/memutil/BUILD +++ b/pkg/sentry/memutil/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "memutil", srcs = [ diff --git a/pkg/sentry/platform/interrupt/BUILD b/pkg/sentry/platform/interrupt/BUILD index 35121321a..dbafa3204 100644 --- a/pkg/sentry/platform/interrupt/BUILD +++ b/pkg/sentry/platform/interrupt/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "interrupt", srcs = [ diff --git a/pkg/sentry/platform/kvm/BUILD b/pkg/sentry/platform/kvm/BUILD index 4ef9e20d7..1b71e629f 100644 --- a/pkg/sentry/platform/kvm/BUILD +++ b/pkg/sentry/platform/kvm/BUILD @@ -1,6 +1,7 @@ +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template_instance") go_template_instance( diff --git a/pkg/sentry/platform/kvm/testutil/BUILD b/pkg/sentry/platform/kvm/testutil/BUILD index e779e3893..1dffe94a4 100644 --- a/pkg/sentry/platform/kvm/testutil/BUILD +++ b/pkg/sentry/platform/kvm/testutil/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "testutil", testonly = 1, diff --git a/pkg/sentry/platform/procid/BUILD b/pkg/sentry/platform/procid/BUILD index ba68d48f4..d3398d1e8 100644 --- a/pkg/sentry/platform/procid/BUILD +++ b/pkg/sentry/platform/procid/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "procid", srcs = [ diff --git a/pkg/sentry/platform/ptrace/BUILD b/pkg/sentry/platform/ptrace/BUILD index debae058b..2eb354ad4 100644 --- a/pkg/sentry/platform/ptrace/BUILD +++ b/pkg/sentry/platform/ptrace/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "ptrace", srcs = [ diff --git a/pkg/sentry/platform/ring0/BUILD b/pkg/sentry/platform/ring0/BUILD index 2485eb2eb..c35d49f2d 100644 --- a/pkg/sentry/platform/ring0/BUILD +++ b/pkg/sentry/platform/ring0/BUILD @@ -1,6 +1,7 @@ +load("//tools/go_stateify:defs.bzl", "go_library") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library") load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") go_template( diff --git a/pkg/sentry/platform/ring0/gen_offsets/BUILD b/pkg/sentry/platform/ring0/gen_offsets/BUILD index 3bce56985..b76d7974e 100644 --- a/pkg/sentry/platform/ring0/gen_offsets/BUILD +++ b/pkg/sentry/platform/ring0/gen_offsets/BUILD @@ -1,6 +1,7 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + package(licenses = ["notice"]) # Apache 2.0 -load("@io_bazel_rules_go//go:def.bzl", "go_binary") load("//tools/go_generics:defs.bzl", "go_template_instance") go_template_instance( diff --git a/pkg/sentry/platform/ring0/pagetables/BUILD b/pkg/sentry/platform/ring0/pagetables/BUILD index 7a86e2234..de1b920af 100644 --- a/pkg/sentry/platform/ring0/pagetables/BUILD +++ b/pkg/sentry/platform/ring0/pagetables/BUILD @@ -1,6 +1,7 @@ +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") go_template( diff --git a/pkg/sentry/platform/safecopy/BUILD b/pkg/sentry/platform/safecopy/BUILD index 7dcf6e561..614d9e21e 100644 --- a/pkg/sentry/platform/safecopy/BUILD +++ b/pkg/sentry/platform/safecopy/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "safecopy", srcs = [ diff --git a/pkg/sentry/safemem/BUILD b/pkg/sentry/safemem/BUILD index e96509ce1..87a9bff12 100644 --- a/pkg/sentry/safemem/BUILD +++ b/pkg/sentry/safemem/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "safemem", srcs = [ diff --git a/pkg/sentry/sighandling/BUILD b/pkg/sentry/sighandling/BUILD index 751176747..41313d334 100644 --- a/pkg/sentry/sighandling/BUILD +++ b/pkg/sentry/sighandling/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "sighandling", srcs = [ diff --git a/pkg/sentry/socket/rpcinet/BUILD b/pkg/sentry/socket/rpcinet/BUILD index 38fa54283..06e121946 100644 --- a/pkg/sentry/socket/rpcinet/BUILD +++ b/pkg/sentry/socket/rpcinet/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "rpcinet", srcs = [ diff --git a/pkg/sentry/socket/rpcinet/conn/BUILD b/pkg/sentry/socket/rpcinet/conn/BUILD index c51ca14b1..a16977f29 100644 --- a/pkg/sentry/socket/rpcinet/conn/BUILD +++ b/pkg/sentry/socket/rpcinet/conn/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # BSD - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # BSD + go_library( name = "conn", srcs = ["conn.go"], diff --git a/pkg/sentry/socket/rpcinet/notifier/BUILD b/pkg/sentry/socket/rpcinet/notifier/BUILD index 2ae902b3f..2bab01774 100644 --- a/pkg/sentry/socket/rpcinet/notifier/BUILD +++ b/pkg/sentry/socket/rpcinet/notifier/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # BSD - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # BSD + go_library( name = "notifier", srcs = ["notifier.go"], diff --git a/pkg/sentry/state/BUILD b/pkg/sentry/state/BUILD index a57a8298e..f1f6fdb7d 100644 --- a/pkg/sentry/state/BUILD +++ b/pkg/sentry/state/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "state", srcs = [ diff --git a/pkg/sentry/strace/BUILD b/pkg/sentry/strace/BUILD index 674554081..52c7f325c 100644 --- a/pkg/sentry/strace/BUILD +++ b/pkg/sentry/strace/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "strace", srcs = [ diff --git a/pkg/sentry/syscalls/BUILD b/pkg/sentry/syscalls/BUILD index 2a9f0915e..35192ff49 100644 --- a/pkg/sentry/syscalls/BUILD +++ b/pkg/sentry/syscalls/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "syscalls", srcs = [ diff --git a/pkg/sentry/time/BUILD b/pkg/sentry/time/BUILD index 9452787fb..5dadb8a2d 100644 --- a/pkg/sentry/time/BUILD +++ b/pkg/sentry/time/BUILD @@ -1,6 +1,7 @@ +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template_instance") go_template_instance( diff --git a/pkg/sentry/unimpl/BUILD b/pkg/sentry/unimpl/BUILD index 63da5e81f..42e24ace5 100644 --- a/pkg/sentry/unimpl/BUILD +++ b/pkg/sentry/unimpl/BUILD @@ -1,8 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +package(licenses = ["notice"]) # Apache 2.0 + proto_library( name = "unimplemented_syscall_proto", srcs = ["unimplemented_syscall.proto"], diff --git a/pkg/sentry/uniqueid/BUILD b/pkg/sentry/uniqueid/BUILD index 68b82af47..0929497c3 100644 --- a/pkg/sentry/uniqueid/BUILD +++ b/pkg/sentry/uniqueid/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "uniqueid", srcs = ["context.go"], diff --git a/pkg/sentry/watchdog/BUILD b/pkg/sentry/watchdog/BUILD index 13bc33eb1..b2c687b20 100644 --- a/pkg/sentry/watchdog/BUILD +++ b/pkg/sentry/watchdog/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "watchdog", srcs = ["watchdog.go"], diff --git a/pkg/sleep/BUILD b/pkg/sleep/BUILD index 05e4ca540..338fd9336 100644 --- a/pkg/sleep/BUILD +++ b/pkg/sleep/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "sleep", srcs = [ diff --git a/pkg/state/BUILD b/pkg/state/BUILD index 6a5b2d4ff..dd0f250fa 100644 --- a/pkg/state/BUILD +++ b/pkg/state/BUILD @@ -1,7 +1,8 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") + +package(licenses = ["notice"]) # Apache 2.0 + load("//tools/go_generics:defs.bzl", "go_template_instance") go_template_instance( diff --git a/pkg/state/statefile/BUILD b/pkg/state/statefile/BUILD index 6be78dc9b..66c8f3807 100644 --- a/pkg/state/statefile/BUILD +++ b/pkg/state/statefile/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "statefile", srcs = ["statefile.go"], diff --git a/pkg/sync/atomicptrtest/BUILD b/pkg/sync/atomicptrtest/BUILD index 4fa959df0..9cb7f66fe 100644 --- a/pkg/sync/atomicptrtest/BUILD +++ b/pkg/sync/atomicptrtest/BUILD @@ -1,6 +1,7 @@ +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template_instance") go_template_instance( diff --git a/pkg/sync/seqatomictest/BUILD b/pkg/sync/seqatomictest/BUILD index 07b4f85ab..54f8e59b1 100644 --- a/pkg/sync/seqatomictest/BUILD +++ b/pkg/sync/seqatomictest/BUILD @@ -1,6 +1,7 @@ +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") + package(licenses = ["notice"]) # Apache 2.0 -load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template_instance") go_template_instance( diff --git a/pkg/syserr/BUILD b/pkg/syserr/BUILD index 5dd2e90bb..30ae20772 100644 --- a/pkg/syserr/BUILD +++ b/pkg/syserr/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "syserr", srcs = [ diff --git a/pkg/syserror/BUILD b/pkg/syserror/BUILD index e050c2043..d4c6da97a 100644 --- a/pkg/syserror/BUILD +++ b/pkg/syserror/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "syserror", srcs = ["syserror.go"], diff --git a/pkg/tcpip/adapters/gonet/BUILD b/pkg/tcpip/adapters/gonet/BUILD index bf618831a..723ad668f 100644 --- a/pkg/tcpip/adapters/gonet/BUILD +++ b/pkg/tcpip/adapters/gonet/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "gonet", srcs = ["gonet.go"], diff --git a/pkg/tcpip/checker/BUILD b/pkg/tcpip/checker/BUILD index e8a524918..a1de808b9 100644 --- a/pkg/tcpip/checker/BUILD +++ b/pkg/tcpip/checker/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "checker", testonly = 1, diff --git a/pkg/tcpip/link/channel/BUILD b/pkg/tcpip/link/channel/BUILD index 9a6f49c45..25f6c1457 100644 --- a/pkg/tcpip/link/channel/BUILD +++ b/pkg/tcpip/link/channel/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "channel", srcs = ["channel.go"], diff --git a/pkg/tcpip/link/fdbased/BUILD b/pkg/tcpip/link/fdbased/BUILD index 6e75e9f47..94391433c 100644 --- a/pkg/tcpip/link/fdbased/BUILD +++ b/pkg/tcpip/link/fdbased/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "fdbased", srcs = ["endpoint.go"], diff --git a/pkg/tcpip/link/loopback/BUILD b/pkg/tcpip/link/loopback/BUILD index cc4247ffd..a46ba7f11 100644 --- a/pkg/tcpip/link/loopback/BUILD +++ b/pkg/tcpip/link/loopback/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "loopback", srcs = ["loopback.go"], diff --git a/pkg/tcpip/link/rawfile/BUILD b/pkg/tcpip/link/rawfile/BUILD index 10b35a37e..829ea7c42 100644 --- a/pkg/tcpip/link/rawfile/BUILD +++ b/pkg/tcpip/link/rawfile/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "rawfile", srcs = [ diff --git a/pkg/tcpip/link/sharedmem/BUILD b/pkg/tcpip/link/sharedmem/BUILD index 5390257c5..d7f1e66ef 100644 --- a/pkg/tcpip/link/sharedmem/BUILD +++ b/pkg/tcpip/link/sharedmem/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "sharedmem", srcs = [ diff --git a/pkg/tcpip/link/sharedmem/pipe/BUILD b/pkg/tcpip/link/sharedmem/pipe/BUILD index ff798ae6f..12e813509 100644 --- a/pkg/tcpip/link/sharedmem/pipe/BUILD +++ b/pkg/tcpip/link/sharedmem/pipe/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "pipe", srcs = [ diff --git a/pkg/tcpip/link/sharedmem/queue/BUILD b/pkg/tcpip/link/sharedmem/queue/BUILD index c4a7879c4..661037bb2 100644 --- a/pkg/tcpip/link/sharedmem/queue/BUILD +++ b/pkg/tcpip/link/sharedmem/queue/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "queue", srcs = [ diff --git a/pkg/tcpip/link/sniffer/BUILD b/pkg/tcpip/link/sniffer/BUILD index 7155aea66..52e237c25 100644 --- a/pkg/tcpip/link/sniffer/BUILD +++ b/pkg/tcpip/link/sniffer/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "sniffer", srcs = [ diff --git a/pkg/tcpip/link/tun/BUILD b/pkg/tcpip/link/tun/BUILD index a8bb03661..5ec01cec9 100644 --- a/pkg/tcpip/link/tun/BUILD +++ b/pkg/tcpip/link/tun/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "tun", srcs = ["tun_unsafe.go"], diff --git a/pkg/tcpip/link/waitable/BUILD b/pkg/tcpip/link/waitable/BUILD index 7582df32e..ba495c437 100644 --- a/pkg/tcpip/link/waitable/BUILD +++ b/pkg/tcpip/link/waitable/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "waitable", srcs = [ diff --git a/pkg/tcpip/network/BUILD b/pkg/tcpip/network/BUILD index 25a3c98b6..a2a07f533 100644 --- a/pkg/tcpip/network/BUILD +++ b/pkg/tcpip/network/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_test( name = "ip_test", size = "small", diff --git a/pkg/tcpip/network/arp/BUILD b/pkg/tcpip/network/arp/BUILD index 44f2b66e5..f6fb7daf7 100644 --- a/pkg/tcpip/network/arp/BUILD +++ b/pkg/tcpip/network/arp/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "arp", srcs = ["arp.go"], diff --git a/pkg/tcpip/network/hash/BUILD b/pkg/tcpip/network/hash/BUILD index 1c22c52fc..401dce646 100644 --- a/pkg/tcpip/network/hash/BUILD +++ b/pkg/tcpip/network/hash/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "hash", srcs = ["hash.go"], diff --git a/pkg/tcpip/network/ipv4/BUILD b/pkg/tcpip/network/ipv4/BUILD index 90d65d531..e72317e9f 100644 --- a/pkg/tcpip/network/ipv4/BUILD +++ b/pkg/tcpip/network/ipv4/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "ipv4", srcs = [ diff --git a/pkg/tcpip/network/ipv6/BUILD b/pkg/tcpip/network/ipv6/BUILD index 2f19a659e..808c37df3 100644 --- a/pkg/tcpip/network/ipv6/BUILD +++ b/pkg/tcpip/network/ipv6/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "ipv6", srcs = [ diff --git a/pkg/tcpip/ports/BUILD b/pkg/tcpip/ports/BUILD index 3c3374275..c69fc0744 100644 --- a/pkg/tcpip/ports/BUILD +++ b/pkg/tcpip/ports/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "ports", srcs = ["ports.go"], diff --git a/pkg/tcpip/sample/tun_tcp_connect/BUILD b/pkg/tcpip/sample/tun_tcp_connect/BUILD index 21d32245d..32baf2115 100644 --- a/pkg/tcpip/sample/tun_tcp_connect/BUILD +++ b/pkg/tcpip/sample/tun_tcp_connect/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_binary") +package(licenses = ["notice"]) # Apache 2.0 + go_binary( name = "tun_tcp_connect", srcs = ["main.go"], diff --git a/pkg/tcpip/sample/tun_tcp_echo/BUILD b/pkg/tcpip/sample/tun_tcp_echo/BUILD index d7402aaa2..760445843 100644 --- a/pkg/tcpip/sample/tun_tcp_echo/BUILD +++ b/pkg/tcpip/sample/tun_tcp_echo/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_binary") +package(licenses = ["notice"]) # Apache 2.0 + go_binary( name = "tun_tcp_echo", srcs = ["main.go"], diff --git a/pkg/tcpip/transport/tcp/testing/context/BUILD b/pkg/tcpip/transport/tcp/testing/context/BUILD index 7a95594ef..814e5c1ea 100644 --- a/pkg/tcpip/transport/tcp/testing/context/BUILD +++ b/pkg/tcpip/transport/tcp/testing/context/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "context", testonly = 1, diff --git a/pkg/tcpip/transport/tcpconntrack/BUILD b/pkg/tcpip/transport/tcpconntrack/BUILD index 46da3e6f1..ac1a94d4d 100644 --- a/pkg/tcpip/transport/tcpconntrack/BUILD +++ b/pkg/tcpip/transport/tcpconntrack/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "tcpconntrack", srcs = ["tcp_conntrack.go"], diff --git a/pkg/tmutex/BUILD b/pkg/tmutex/BUILD index d18338fff..c20df7005 100644 --- a/pkg/tmutex/BUILD +++ b/pkg/tmutex/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "tmutex", srcs = ["tmutex.go"], diff --git a/pkg/unet/BUILD b/pkg/unet/BUILD index acdfd7cb6..f90e43c89 100644 --- a/pkg/unet/BUILD +++ b/pkg/unet/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "unet", srcs = [ diff --git a/pkg/urpc/BUILD b/pkg/urpc/BUILD index d32c57d1a..21008cf6c 100644 --- a/pkg/urpc/BUILD +++ b/pkg/urpc/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "urpc", srcs = ["urpc.go"], diff --git a/pkg/waiter/fdnotifier/BUILD b/pkg/waiter/fdnotifier/BUILD index 4e582755d..af6baa303 100644 --- a/pkg/waiter/fdnotifier/BUILD +++ b/pkg/waiter/fdnotifier/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("//tools/go_stateify:defs.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "fdnotifier", srcs = [ diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index 04cc0e854..07afce807 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "boot", srcs = [ diff --git a/runsc/boot/filter/BUILD b/runsc/boot/filter/BUILD index 48f2c8024..004222242 100644 --- a/runsc/boot/filter/BUILD +++ b/runsc/boot/filter/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "filter", srcs = [ diff --git a/runsc/cgroup/BUILD b/runsc/cgroup/BUILD index 10a8e5feb..bf2f373a9 100644 --- a/runsc/cgroup/BUILD +++ b/runsc/cgroup/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "cgroup", srcs = ["cgroup.go"], diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 7040eb4ec..394bb0e1f 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "cmd", srcs = [ diff --git a/runsc/console/BUILD b/runsc/console/BUILD index fa1a7d430..ff4ccff69 100644 --- a/runsc/console/BUILD +++ b/runsc/console/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "console", srcs = ["console.go"], diff --git a/runsc/container/BUILD b/runsc/container/BUILD index f4c6f1525..bdd93aaba 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "container", srcs = [ diff --git a/runsc/fsgofer/BUILD b/runsc/fsgofer/BUILD index 24e172f48..f28e4fa77 100644 --- a/runsc/fsgofer/BUILD +++ b/runsc/fsgofer/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "fsgofer", srcs = [ diff --git a/runsc/fsgofer/filter/BUILD b/runsc/fsgofer/filter/BUILD index 40f4f2205..c7848d10c 100644 --- a/runsc/fsgofer/filter/BUILD +++ b/runsc/fsgofer/filter/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "filter", srcs = [ diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go index e03bb7752..fd913831a 100644 --- a/runsc/fsgofer/fsgofer.go +++ b/runsc/fsgofer/fsgofer.go @@ -26,7 +26,6 @@ import ( "math" "os" "path" - "strings" "sync" "syscall" @@ -181,18 +180,6 @@ func (a *attachPoint) makeQID(stat syscall.Stat_t) p9.QID { } } -func isNameValid(name string) bool { - if name == "" || name == "." || name == ".." { - log.Warningf("Invalid name: %s", name) - return false - } - if strings.IndexByte(name, '/') >= 0 { - log.Warningf("Invalid name: %s", name) - return false - } - return true -} - // localFile implements p9.File wrapping a local file. The underlying file // is opened during Walk() and stored in 'controlFile' to be used with other // operations. The mode in which the file is opened varies depending on the @@ -228,11 +215,7 @@ type localFile struct { // attachPoint is the attachPoint that serves this localFile. attachPoint *attachPoint - // mu protects 'hostPath' when file is renamed. - mu sync.Mutex - - // TODO: hostPath is not safe to use as path needs to be walked - // everytime (and can change underneath us). Remove all usages. + // hostPath will be safely updated by the Renamed hook. hostPath string // controlFile is opened when localFile is created and it's never nil. @@ -246,6 +229,7 @@ type localFile struct { // if localFile isn't opened. mode p9.OpenFlags + // ft is the fileType for this file. ft fileType // readDirMu protects against concurrent Readdir calls. @@ -296,10 +280,7 @@ func openAnyFile(parent *localFile, name string) (*os.File, string, error) { return nil, "", extractErrno(err) } - parent.mu.Lock() - defer parent.mu.Unlock() newPath := path.Join(parent.hostPath, name) - return os.NewFile(uintptr(fd), newPath), newPath, nil } @@ -382,13 +363,10 @@ func (l *localFile) Open(mode p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) { log.Debugf("Open reopening file, mode: %v, %q", mode, l.controlFile.Name()) var err error - l.mu.Lock() newFile, err = os.OpenFile(l.hostPath, openFlags|mode.OSFlags(), 0) if err != nil { - l.mu.Unlock() return nil, p9.QID{}, 0, extractErrno(err) } - l.mu.Unlock() } stat, err := stat(int(newFile.Fd())) @@ -418,9 +396,6 @@ func (l *localFile) Create(name string, mode p9.OpenFlags, perm p9.FileMode, uid } return nil, nil, p9.QID{}, 0, syscall.EBADF } - if !isNameValid(name) { - return nil, nil, p9.QID{}, 0, syscall.EINVAL - } // Use a single file for both 'controlFile' and 'openedFile'. Mode must include read for control // and whichever else was requested by caller. Note that resulting file might have a wider mode @@ -452,9 +427,6 @@ func (l *localFile) Create(name string, mode p9.OpenFlags, perm p9.FileMode, uid return nil, nil, p9.QID{}, 0, extractErrno(err) } - l.mu.Lock() - defer l.mu.Unlock() - cPath := path.Join(l.hostPath, name) f := os.NewFile(uintptr(fd), cPath) c := &localFile{ @@ -477,10 +449,6 @@ func (l *localFile) Mkdir(name string, perm p9.FileMode, uid p9.UID, gid p9.GID) return p9.QID{}, syscall.EBADF } - if !isNameValid(name) { - return p9.QID{}, syscall.EINVAL - } - if err := syscall.Mkdirat(l.controlFD(), name, uint32(perm.Permissions())); err != nil { return p9.QID{}, extractErrno(err) } @@ -517,9 +485,6 @@ func (l *localFile) Walk(names []string) ([]p9.QID, p9.File, error) { return nil, nil, extractErrno(err) } - l.mu.Lock() - defer l.mu.Unlock() - c := &localFile{ attachPoint: l.attachPoint, hostPath: l.hostPath, @@ -532,10 +497,6 @@ func (l *localFile) Walk(names []string) ([]p9.QID, p9.File, error) { var qids []p9.QID last := l for _, name := range names { - if !isNameValid(name) { - return nil, nil, syscall.EINVAL - } - f, path, err := openAnyFile(last, name) if err != nil { return nil, nil, extractErrno(err) @@ -761,15 +722,15 @@ func (l *localFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error { return err } -// Remove implements p9.File. -// -// This is deprecated in favor of UnlinkAt. -func (*localFile) Remove() error { - return syscall.ENOSYS +// Rename implements p9.File; this should never be called. +func (l *localFile) Rename(p9.File, string) error { + panic("rename called directly") } -// Rename implements p9.File. -func (l *localFile) Rename(directory p9.File, name string) error { +// RenameAt implements p9.File.RenameAt. +// +// TODO: change to renameat(2). +func (l *localFile) RenameAt(oldName string, directory p9.File, newName string) error { conf := l.attachPoint.conf if conf.ROMount { if conf.PanicOnWrite { @@ -777,34 +738,16 @@ func (l *localFile) Rename(directory p9.File, name string) error { } return syscall.EBADF } - if !isNameValid(name) { - return syscall.EINVAL - } - - l.mu.Lock() - defer l.mu.Unlock() - // TODO: change to renameat(2) - parent := directory.(*localFile) - newPath := path.Join(parent.hostPath, name) - if err := syscall.Rename(l.hostPath, newPath); err != nil { + newParent := directory.(*localFile) + oldPath := path.Join(l.hostPath, oldName) + newPath := path.Join(newParent.hostPath, newName) + if err := syscall.Rename(oldPath, newPath); err != nil { return extractErrno(err) } - - // Update path on success. - // TODO: this doesn't cover cases where any of the - // parents have been renamed. - l.hostPath = newPath return nil } -// RenameAt implements p9.File.RenameAt. -// -// Code still uses [deprecated] Rename(). -func (*localFile) RenameAt(_ string, _ p9.File, _ string) error { - return syscall.ENOSYS -} - // ReadAt implements p9.File. func (l *localFile) ReadAt(p []byte, offset uint64) (int, error) { if l.mode != p9.ReadOnly && l.mode != p9.ReadWrite { @@ -848,9 +791,6 @@ func (l *localFile) Symlink(target, newName string, uid p9.UID, gid p9.GID) (p9. } return p9.QID{}, syscall.EBADF } - if !isNameValid(newName) { - return p9.QID{}, syscall.EINVAL - } if err := unix.Symlinkat(target, l.controlFD(), newName); err != nil { return p9.QID{}, extractErrno(err) @@ -882,9 +822,6 @@ func (l *localFile) Link(target p9.File, newName string) error { } return syscall.EBADF } - if !isNameValid(newName) { - return syscall.EINVAL - } targetFile := target.(*localFile) if err := unix.Linkat(targetFile.controlFD(), "", l.controlFD(), newName, linux.AT_EMPTY_PATH); err != nil { @@ -909,9 +846,7 @@ func (l *localFile) UnlinkAt(name string, flags uint32) error { } return syscall.EBADF } - if !isNameValid(name) { - return syscall.EINVAL - } + if err := unix.Unlinkat(l.controlFD(), name, int(flags)); err != nil { return extractErrno(err) } @@ -1000,6 +935,11 @@ func (l *localFile) Close() error { return err } +// Renamed implements p9.Renamed. +func (l *localFile) Renamed(newDir p9.File, newName string) { + l.hostPath = path.Join(newDir.(*localFile).hostPath, newName) +} + // extractErrno tries to determine the errno. func extractErrno(err error) syscall.Errno { if err == nil { diff --git a/runsc/fsgofer/fsgofer_test.go b/runsc/fsgofer/fsgofer_test.go index 48860f952..34033245b 100644 --- a/runsc/fsgofer/fsgofer_test.go +++ b/runsc/fsgofer/fsgofer_test.go @@ -415,22 +415,22 @@ func TestLink(t *testing.T) { func TestROMountChecks(t *testing.T) { runCustom(t, allTypes, roConfs, func(t *testing.T, s state) { - if _, _, _, _, err := s.file.Create("..", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF { + if _, _, _, _, err := s.file.Create("some_file", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF { t.Errorf("%v: Create() should have failed, got: %v, expected: syscall.EBADF", s, err) } - if _, err := s.file.Mkdir("..", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF { + if _, err := s.file.Mkdir("some_dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF { t.Errorf("%v: MkDir() should have failed, got: %v, expected: syscall.EBADF", s, err) } - if err := s.file.Rename(s.file, ".."); err != syscall.EBADF { + if err := s.file.RenameAt("some_file", s.file, "other_file"); err != syscall.EBADF { t.Errorf("%v: Rename() should have failed, got: %v, expected: syscall.EBADF", s, err) } - if _, err := s.file.Symlink("some_place", "..", p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF { + if _, err := s.file.Symlink("some_place", "some_symlink", p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF { t.Errorf("%v: Symlink() should have failed, got: %v, expected: syscall.EBADF", s, err) } - if err := s.file.UnlinkAt("..", 0); err != syscall.EBADF { + if err := s.file.UnlinkAt("some_file", 0); err != syscall.EBADF { t.Errorf("%v: UnlinkAt() should have failed, got: %v, expected: syscall.EBADF", s, err) } - if err := s.file.Link(s.file, ".."); err != syscall.EBADF { + if err := s.file.Link(s.file, "some_link"); err != syscall.EBADF { t.Errorf("%v: Link() should have failed, got: %v, expected: syscall.EBADF", s, err) } @@ -445,12 +445,12 @@ func TestROMountChecks(t *testing.T) { func TestROMountPanics(t *testing.T) { conf := Config{ROMount: true, PanicOnWrite: true} runCustom(t, allTypes, []Config{conf}, func(t *testing.T, s state) { - assertPanic(t, func() { s.file.Create("..", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())) }) - assertPanic(t, func() { s.file.Mkdir("..", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())) }) - assertPanic(t, func() { s.file.Rename(s.file, "..") }) - assertPanic(t, func() { s.file.Symlink("some_place", "..", p9.UID(os.Getuid()), p9.GID(os.Getgid())) }) - assertPanic(t, func() { s.file.UnlinkAt("..", 0) }) - assertPanic(t, func() { s.file.Link(s.file, "..") }) + assertPanic(t, func() { s.file.Create("some_file", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())) }) + assertPanic(t, func() { s.file.Mkdir("some_dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())) }) + assertPanic(t, func() { s.file.RenameAt("some_file", s.file, "other_file") }) + assertPanic(t, func() { s.file.Symlink("some_place", "some_symlink", p9.UID(os.Getuid()), p9.GID(os.Getgid())) }) + assertPanic(t, func() { s.file.UnlinkAt("some_file", 0) }) + assertPanic(t, func() { s.file.Link(s.file, "some_link") }) valid := p9.SetAttrMask{Size: true} attr := p9.SetAttr{Size: 0} @@ -458,60 +458,6 @@ func TestROMountPanics(t *testing.T) { }) } -func TestInvalidName(t *testing.T) { - runCustom(t, []fileType{regular}, rwConfs, func(t *testing.T, s state) { - if _, _, _, _, err := s.file.Create("..", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EINVAL { - t.Errorf("%v: Create() should have failed, got: %v, expected: syscall.EINVAL", s, err) - } - if _, _, err := s.file.Walk([]string{".."}); err != syscall.EINVAL { - t.Errorf("%v: Walk() should have failed, got: %v, expected: syscall.EINVAL", s, err) - } - if _, err := s.file.Mkdir("..", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EINVAL { - t.Errorf("%v: MkDir() should have failed, got: %v, expected: syscall.EINVAL", s, err) - } - if err := s.file.Rename(s.file, ".."); err != syscall.EINVAL { - t.Errorf("%v: Rename() should have failed, got: %v, expected: syscall.EINVAL", s, err) - } - if _, err := s.file.Symlink("some_place", "..", p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EINVAL { - t.Errorf("%v: Symlink() should have failed, got: %v, expected: syscall.EINVAL", s, err) - } - if err := s.file.UnlinkAt("..", 0); err != syscall.EINVAL { - t.Errorf("%v: UnlinkAt() should have failed, got: %v, expected: syscall.EINVAL", s, err) - } - if err := s.file.Link(s.file, ".."); err != syscall.EINVAL { - t.Errorf("%v: Link() should have failed, got: %v, expected: syscall.EINVAL", s, err) - } - }) -} - -func TestIsNameValid(t *testing.T) { - valid := []string{ - "name", - "123", - "!@#$%^&*()", - ".name", - "..name", - "...", - } - for _, s := range valid { - if got := isNameValid(s); !got { - t.Errorf("isNameValid(%s) failed, got: %v, expected: true", s, got) - } - } - invalid := []string{ - ".", - "..", - "name/name", - "/name", - "name/", - } - for _, s := range invalid { - if got := isNameValid(s); got { - t.Errorf("isNameValid(%s) failed, got: %v, expected: false", s, got) - } - } -} - func TestWalkNotFound(t *testing.T) { runCustom(t, []fileType{directory}, allConfs, func(t *testing.T, s state) { if _, _, err := s.file.Walk([]string{"nobody-here"}); err != syscall.ENOENT { diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index eb9c4cd76..d6043bcf7 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "sandbox", srcs = [ diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index e73b2293f..a1e5da3f5 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "specutils", srcs = [ diff --git a/runsc/test/image/BUILD b/runsc/test/image/BUILD index c41161d50..22b3ebd2a 100644 --- a/runsc/test/image/BUILD +++ b/runsc/test/image/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_test( name = "image_test", size = "large", diff --git a/runsc/test/integration/BUILD b/runsc/test/integration/BUILD index 726ebf49e..e7204dc66 100644 --- a/runsc/test/integration/BUILD +++ b/runsc/test/integration/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_test( name = "integration_test", size = "large", diff --git a/runsc/test/root/BUILD b/runsc/test/root/BUILD index c69249b52..c2567ef23 100644 --- a/runsc/test/root/BUILD +++ b/runsc/test/root/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "root", srcs = ["root.go"], diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index da2535bfa..128bd80fb 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "testutil", srcs = [ diff --git a/runsc/tools/dockercfg/BUILD b/runsc/tools/dockercfg/BUILD index 5abb0c90a..a80b3abab 100644 --- a/runsc/tools/dockercfg/BUILD +++ b/runsc/tools/dockercfg/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_binary") +package(licenses = ["notice"]) # Apache 2.0 + go_binary( name = "dockercfg", srcs = ["dockercfg.go"], diff --git a/tools/go_generics/BUILD b/tools/go_generics/BUILD index 1afc58625..22c2e62c3 100644 --- a/tools/go_generics/BUILD +++ b/tools/go_generics/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_binary") +package(licenses = ["notice"]) # Apache 2.0 + go_binary( name = "go_generics", srcs = [ diff --git a/tools/go_generics/globals/BUILD b/tools/go_generics/globals/BUILD index a238becab..c26ac56d2 100644 --- a/tools/go_generics/globals/BUILD +++ b/tools/go_generics/globals/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_library") +package(licenses = ["notice"]) # Apache 2.0 + go_library( name = "globals", srcs = [ diff --git a/tools/go_generics/rules_tests/BUILD b/tools/go_generics/rules_tests/BUILD index 2d9a6fa9d..23b2d656d 100644 --- a/tools/go_generics/rules_tests/BUILD +++ b/tools/go_generics/rules_tests/BUILD @@ -1,6 +1,7 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + package(licenses = ["notice"]) # Apache 2.0 -load("@io_bazel_rules_go//go:def.bzl", "go_test") load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") go_template_instance( diff --git a/tools/go_stateify/BUILD b/tools/go_stateify/BUILD index edbeb4e2d..68d37f5d7 100644 --- a/tools/go_stateify/BUILD +++ b/tools/go_stateify/BUILD @@ -1,7 +1,7 @@ -package(licenses = ["notice"]) # Apache 2.0 - load("@io_bazel_rules_go//go:def.bzl", "go_binary") +package(licenses = ["notice"]) # Apache 2.0 + go_binary( name = "stateify", srcs = ["main.go"], -- cgit v1.2.3 From 7f558eda44bf93c31dfbbe621c2bb84d55b5701f Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Tue, 13 Nov 2018 15:16:11 -0800 Subject: Internal change. PiperOrigin-RevId: 221343421 Change-Id: I418b5204c5ed4fe1e0af25ef36ee66b9b571928e --- runsc/specutils/specutils.go | 15 +++++++++++++++ runsc/test/testutil/testutil.go | 1 + 2 files changed, 16 insertions(+) (limited to 'runsc/specutils') diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index ab14ed1fc..0e0961801 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -216,6 +216,21 @@ func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, er return &caps, nil } +// AllCapabilities returns a LinuxCapabilities struct with all capabilities. +func AllCapabilities() *specs.LinuxCapabilities { + var names []string + for n := range capFromName { + names = append(names, n) + } + return &specs.LinuxCapabilities{ + Bounding: names, + Effective: names, + Inheritable: names, + Permitted: names, + Ambient: names, + } +} + var capFromName = map[string]linux.Capability{ "CAP_CHOWN": linux.CAP_CHOWN, "CAP_DAC_OVERRIDE": linux.CAP_DAC_OVERRIDE, diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index d323d7899..c816de3f0 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -139,6 +139,7 @@ func NewSpecWithArgs(args ...string) *specs.Spec { Env: []string{ "PATH=" + os.Getenv("PATH"), }, + Capabilities: specutils.AllCapabilities(), }, Mounts: []specs.Mount{ // Root is readonly, but many tests want to write to tmpdir. -- cgit v1.2.3 From 7b938ef78c2b180d1d0554534972069ec393322d Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Wed, 14 Nov 2018 09:57:34 -0800 Subject: Internal change. PiperOrigin-RevId: 221462069 Change-Id: Id469ed21fe12e582c78340189b932989afa13c67 --- runsc/specutils/BUILD | 1 + runsc/specutils/specutils.go | 1 + 2 files changed, 2 insertions(+) (limited to 'runsc/specutils') diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index a1e5da3f5..034628c92 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -11,6 +11,7 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/specutils", visibility = [ "//runsc:__subpackages__", + "//third_party/golang/gvisor/test:__subpackages__", ], deps = [ "//pkg/abi/linux", diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 0e0961801..055076475 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -52,6 +52,7 @@ func LogSpec(spec *specs.Spec) { } log.Debugf("Spec.Process: %+v", spec.Process) log.Debugf("Spec.Root: %+v", spec.Root) + log.Debugf("Spec.Mounts: %+v", spec.Mounts) } // ValidateSpec validates that the spec is compatible with runsc. -- cgit v1.2.3 From 845836c5783cb237c28b91f2f9a8f52a8219228e Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Fri, 16 Nov 2018 14:01:46 -0800 Subject: Internal change. PiperOrigin-RevId: 221848471 Change-Id: I882fbe5ce7737048b2e1f668848e9c14ed355665 --- runsc/boot/BUILD | 1 - runsc/container/BUILD | 1 - runsc/specutils/BUILD | 1 - runsc/test/testutil/BUILD | 1 - 4 files changed, 4 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index bdaa7a0c3..07afce807 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -21,7 +21,6 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/boot", visibility = [ "//runsc:__subpackages__", - "//third_party/golang/gvisor/test:__subpackages__", ], deps = [ "//pkg/abi", diff --git a/runsc/container/BUILD b/runsc/container/BUILD index 551e5bc99..bdd93aaba 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -13,7 +13,6 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/container", visibility = [ "//runsc:__subpackages__", - "//third_party/golang/gvisor/test:__subpackages__", ], deps = [ "//pkg/log", diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 034628c92..a1e5da3f5 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -11,7 +11,6 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/specutils", visibility = [ "//runsc:__subpackages__", - "//third_party/golang/gvisor/test:__subpackages__", ], deps = [ "//pkg/abi/linux", diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index 8d4839318..3ed235393 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -13,7 +13,6 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/test/testutil", visibility = [ "//runsc:__subpackages__", - "//third_party/golang/gvisor/test:__subpackages__", ], deps = [ "//runsc/boot", -- cgit v1.2.3 From d3bc79bc8438206ac6a14fde4eaa288fc07eee82 Mon Sep 17 00:00:00 2001 From: Brian Geffon Date: Mon, 10 Dec 2018 14:41:40 -0800 Subject: Open source system call tests. PiperOrigin-RevId: 224886231 Change-Id: I0fccb4d994601739d8b16b1d4e6b31f40297fb22 --- kokoro/run_tests.sh | 9 +- runsc/boot/BUILD | 1 + runsc/container/BUILD | 1 + runsc/specutils/BUILD | 1 + runsc/test/testutil/BUILD | 1 + test/syscalls/BUILD | 522 ++++ test/syscalls/README.md | 110 + test/syscalls/build_defs.bzl | 54 + test/syscalls/gtest/BUILD | 12 + test/syscalls/gtest/gtest.go | 93 + test/syscalls/linux/32bit.cc | 226 ++ test/syscalls/linux/BUILD | 2951 ++++++++++++++++++++ test/syscalls/linux/accept_bind.cc | 600 ++++ test/syscalls/linux/accept_bind_stream.cc | 93 + test/syscalls/linux/access.cc | 170 ++ test/syscalls/linux/affinity.cc | 241 ++ test/syscalls/linux/aio.cc | 433 +++ test/syscalls/linux/alarm.cc | 193 ++ test/syscalls/linux/arch_prctl.cc | 48 + test/syscalls/linux/bad.cc | 39 + test/syscalls/linux/base_poll_test.cc | 65 + test/syscalls/linux/base_poll_test.h | 101 + test/syscalls/linux/bind.cc | 146 + test/syscalls/linux/brk.cc | 31 + test/syscalls/linux/chdir.cc | 69 + test/syscalls/linux/chmod.cc | 262 ++ test/syscalls/linux/chown.cc | 200 ++ test/syscalls/linux/chroot.cc | 364 +++ test/syscalls/linux/clock_getres.cc | 37 + test/syscalls/linux/clock_gettime.cc | 156 ++ test/syscalls/linux/clock_nanosleep.cc | 153 + test/syscalls/linux/concurrency.cc | 124 + test/syscalls/linux/creat.cc | 57 + test/syscalls/linux/dev.cc | 149 + test/syscalls/linux/dup.cc | 139 + test/syscalls/linux/epoll.cc | 468 ++++ test/syscalls/linux/eventfd.cc | 189 ++ test/syscalls/linux/exceptions.cc | 146 + test/syscalls/linux/exec.cc | 625 +++++ test/syscalls/linux/exec.h | 34 + test/syscalls/linux/exec_assert_closed_workload.cc | 45 + test/syscalls/linux/exec_basic_workload.cc | 31 + test/syscalls/linux/exec_binary.cc | 1367 +++++++++ test/syscalls/linux/exec_proc_exe_workload.cc | 35 + test/syscalls/linux/exec_state_workload.cc | 202 ++ test/syscalls/linux/exit.cc | 77 + test/syscalls/linux/exit_script.sh | 22 + test/syscalls/linux/fadvise64.cc | 72 + test/syscalls/linux/fallocate.cc | 57 + test/syscalls/linux/fault.cc | 71 + test/syscalls/linux/fchdir.cc | 77 + test/syscalls/linux/fcntl.cc | 978 +++++++ test/syscalls/linux/file_base.h | 206 ++ test/syscalls/linux/flock.cc | 588 ++++ test/syscalls/linux/fork.cc | 413 +++ test/syscalls/linux/fpsig_fork.cc | 105 + test/syscalls/linux/fpsig_nested.cc | 134 + test/syscalls/linux/fsync.cc | 55 + test/syscalls/linux/futex.cc | 595 ++++ test/syscalls/linux/getcpu.cc | 40 + test/syscalls/linux/getdents.cc | 485 ++++ test/syscalls/linux/getrandom.cc | 61 + test/syscalls/linux/getrusage.cc | 177 ++ test/syscalls/linux/inotify.cc | 1489 ++++++++++ test/syscalls/linux/ioctl.cc | 375 +++ test/syscalls/linux/ip_socket_test_util.cc | 78 + test/syscalls/linux/ip_socket_test_util.h | 57 + test/syscalls/linux/itimer.cc | 342 +++ test/syscalls/linux/kill.cc | 380 +++ test/syscalls/linux/link.cc | 291 ++ test/syscalls/linux/lseek.cc | 202 ++ test/syscalls/linux/madvise.cc | 142 + test/syscalls/linux/memory_accounting.cc | 99 + test/syscalls/linux/mempolicy.cc | 258 ++ test/syscalls/linux/mincore.cc | 96 + test/syscalls/linux/mkdir.cc | 96 + test/syscalls/linux/mknod.cc | 173 ++ test/syscalls/linux/mmap.cc | 1714 ++++++++++++ test/syscalls/linux/mount.cc | 302 ++ test/syscalls/linux/mremap.cc | 514 ++++ test/syscalls/linux/msync.cc | 145 + test/syscalls/linux/munmap.cc | 53 + test/syscalls/linux/open.cc | 340 +++ test/syscalls/linux/open_create.cc | 130 + test/syscalls/linux/partial_bad_buffer.cc | 305 ++ test/syscalls/linux/pause.cc | 88 + test/syscalls/linux/pipe.cc | 480 ++++ test/syscalls/linux/poll.cc | 279 ++ test/syscalls/linux/ppoll.cc | 155 + test/syscalls/linux/prctl.cc | 171 ++ test/syscalls/linux/prctl_setuid.cc | 262 ++ test/syscalls/linux/pread64.cc | 152 + test/syscalls/linux/preadv.cc | 94 + test/syscalls/linux/preadv2.cc | 217 ++ test/syscalls/linux/priority.cc | 215 ++ test/syscalls/linux/priority_execve.cc | 42 + test/syscalls/linux/proc.cc | 1830 ++++++++++++ test/syscalls/linux/proc_net.cc | 59 + test/syscalls/linux/pselect.cc | 190 ++ test/syscalls/linux/ptrace.cc | 948 +++++++ test/syscalls/linux/pty.cc | 1230 ++++++++ test/syscalls/linux/pwrite64.cc | 79 + test/syscalls/linux/read.cc | 117 + test/syscalls/linux/readv.cc | 293 ++ test/syscalls/linux/readv_common.cc | 180 ++ test/syscalls/linux/readv_common.h | 61 + test/syscalls/linux/readv_socket.cc | 182 ++ test/syscalls/linux/rename.cc | 373 +++ test/syscalls/linux/rlimits.cc | 61 + test/syscalls/linux/rtsignal.cc | 172 ++ test/syscalls/linux/sched.cc | 71 + test/syscalls/linux/sched_yield.cc | 33 + test/syscalls/linux/seccomp.cc | 374 +++ test/syscalls/linux/select.cc | 128 + test/syscalls/linux/semaphore.cc | 438 +++ test/syscalls/linux/sendfile.cc | 409 +++ test/syscalls/linux/sendfile_socket.cc | 156 ++ test/syscalls/linux/shm.cc | 445 +++ test/syscalls/linux/sigaction.cc | 70 + test/syscalls/linux/sigaltstack.cc | 274 ++ test/syscalls/linux/sigaltstack_check.cc | 33 + test/syscalls/linux/sigiret.cc | 137 + test/syscalls/linux/sigprocmask.cc | 272 ++ test/syscalls/linux/sigstop.cc | 150 + test/syscalls/linux/sigtimedwait.cc | 248 ++ test/syscalls/linux/socket_abstract.cc | 43 + test/syscalls/linux/socket_filesystem.cc | 43 + test/syscalls/linux/socket_generic.cc | 403 +++ test/syscalls/linux/socket_generic.h | 30 + test/syscalls/linux/socket_inet_loopback.cc | 812 ++++++ test/syscalls/linux/socket_ip_tcp_generic.cc | 392 +++ test/syscalls/linux/socket_ip_tcp_generic.h | 29 + .../linux/socket_ip_tcp_generic_loopback.cc | 47 + test/syscalls/linux/socket_ip_tcp_loopback.cc | 43 + .../linux/socket_ip_tcp_loopback_blocking.cc | 44 + .../linux/socket_ip_tcp_loopback_nonblock.cc | 46 + test/syscalls/linux/socket_ip_tcp_udp_generic.cc | 78 + test/syscalls/linux/socket_ip_udp_loopback.cc | 48 + .../linux/socket_ip_udp_loopback_blocking.cc | 40 + .../linux/socket_ip_udp_loopback_nonblock.cc | 42 + test/syscalls/linux/socket_netdevice.cc | 182 ++ test/syscalls/linux/socket_netlink_route.cc | 314 +++ test/syscalls/linux/socket_netlink_util.cc | 100 + test/syscalls/linux/socket_netlink_util.h | 42 + test/syscalls/linux/socket_non_blocking.cc | 63 + test/syscalls/linux/socket_non_blocking.h | 29 + test/syscalls/linux/socket_non_stream.cc | 174 ++ test/syscalls/linux/socket_non_stream.h | 29 + test/syscalls/linux/socket_non_stream_blocking.cc | 51 + test/syscalls/linux/socket_non_stream_blocking.h | 30 + test/syscalls/linux/socket_stream.cc | 99 + test/syscalls/linux/socket_stream.h | 30 + test/syscalls/linux/socket_stream_blocking.cc | 131 + test/syscalls/linux/socket_stream_blocking.h | 30 + test/syscalls/linux/socket_stream_nonblock.cc | 50 + test/syscalls/linux/socket_stream_nonblock.h | 30 + test/syscalls/linux/socket_test_util.cc | 660 +++++ test/syscalls/linux/socket_test_util.h | 449 +++ test/syscalls/linux/socket_unix.cc | 1181 ++++++++ test/syscalls/linux/socket_unix.h | 29 + test/syscalls/linux/socket_unix_abstract.cc | 38 + .../linux/socket_unix_abstract_nonblock.cc | 38 + test/syscalls/linux/socket_unix_dgram.cc | 45 + test/syscalls/linux/socket_unix_dgram.h | 29 + test/syscalls/linux/socket_unix_dgram_local.cc | 59 + .../linux/socket_unix_dgram_non_blocking.cc | 68 + test/syscalls/linux/socket_unix_domain.cc | 38 + test/syscalls/linux/socket_unix_filesystem.cc | 38 + .../linux/socket_unix_filesystem_nonblock.cc | 38 + test/syscalls/linux/socket_unix_non_stream.cc | 229 ++ test/syscalls/linux/socket_unix_non_stream.h | 30 + .../linux/socket_unix_non_stream_blocking_local.cc | 47 + test/syscalls/linux/socket_unix_pair.cc | 38 + test/syscalls/linux/socket_unix_pair_nonblock.cc | 38 + test/syscalls/linux/socket_unix_seqpacket.cc | 49 + test/syscalls/linux/socket_unix_seqpacket.h | 30 + test/syscalls/linux/socket_unix_seqpacket_local.cc | 59 + test/syscalls/linux/socket_unix_stream.cc | 69 + .../linux/socket_unix_stream_blocking_local.cc | 47 + test/syscalls/linux/socket_unix_stream_local.cc | 49 + .../linux/socket_unix_stream_nonblock_local.cc | 49 + .../syscalls/linux/socket_unix_unbound_abstract.cc | 116 + test/syscalls/linux/socket_unix_unbound_dgram.cc | 162 ++ .../linux/socket_unix_unbound_filesystem.cc | 84 + .../linux/socket_unix_unbound_seqpacket.cc | 91 + test/syscalls/linux/socket_unix_unbound_stream.cc | 738 +++++ test/syscalls/linux/stat.cc | 410 +++ test/syscalls/linux/stat_times.cc | 220 ++ test/syscalls/linux/statfs.cc | 81 + test/syscalls/linux/sticky.cc | 116 + test/syscalls/linux/symlink.cc | 288 ++ test/syscalls/linux/sync.cc | 60 + test/syscalls/linux/sync_file_range.cc | 111 + test/syscalls/linux/sysinfo.cc | 86 + test/syscalls/linux/syslog.cc | 51 + test/syscalls/linux/sysret.cc | 113 + test/syscalls/linux/tcp_socket.cc | 759 +++++ test/syscalls/linux/temp_umask.h | 39 + test/syscalls/linux/tgkill.cc | 48 + test/syscalls/linux/time.cc | 103 + test/syscalls/linux/timerfd.cc | 238 ++ test/syscalls/linux/timers.cc | 642 +++++ test/syscalls/linux/tkill.cc | 75 + test/syscalls/linux/truncate.cc | 217 ++ test/syscalls/linux/udp_bind.cc | 316 +++ test/syscalls/linux/udp_socket.cc | 941 +++++++ test/syscalls/linux/uidgid.cc | 277 ++ test/syscalls/linux/uname.cc | 99 + .../syscalls/linux/unix_domain_socket_test_util.cc | 346 +++ test/syscalls/linux/unix_domain_socket_test_util.h | 161 ++ test/syscalls/linux/unlink.cc | 211 ++ test/syscalls/linux/unshare.cc | 50 + test/syscalls/linux/utimes.cc | 330 +++ test/syscalls/linux/vdso.cc | 48 + test/syscalls/linux/vdso_clock_gettime.cc | 104 + test/syscalls/linux/vfork.cc | 193 ++ test/syscalls/linux/vsyscall.cc | 44 + test/syscalls/linux/wait.cc | 748 +++++ test/syscalls/linux/write.cc | 134 + test/syscalls/syscall_test.go | 245 ++ test/syscalls/syscall_test_runner.sh | 25 + test/util/BUILD | 239 ++ test/util/capability_util.cc | 79 + test/util/capability_util.h | 101 + test/util/cleanup.h | 61 + test/util/file_descriptor.h | 134 + test/util/fs_util.cc | 585 ++++ test/util/fs_util.h | 182 ++ test/util/fs_util_test.cc | 100 + test/util/logging.cc | 97 + test/util/logging.h | 73 + test/util/memory_util.h | 124 + test/util/mount_util.h | 48 + test/util/multiprocess_util.cc | 139 + test/util/multiprocess_util.h | 113 + test/util/posix_error.cc | 93 + test/util/posix_error.h | 428 +++ test/util/posix_error_test.cc | 45 + test/util/proc_util.cc | 98 + test/util/proc_util.h | 150 + test/util/save_util.cc | 59 + test/util/save_util.h | 47 + test/util/signal_util.cc | 103 + test/util/signal_util.h | 92 + test/util/temp_path.cc | 157 ++ test/util/temp_path.h | 134 + test/util/test_main.cc | 20 + test/util/test_util.cc | 248 ++ test/util/test_util.h | 794 ++++++ test/util/test_util_test.cc | 250 ++ test/util/thread_util.h | 89 + test/util/timer_util.cc | 27 + test/util/timer_util.h | 74 + 253 files changed, 55023 insertions(+), 3 deletions(-) create mode 100644 test/syscalls/BUILD create mode 100644 test/syscalls/README.md create mode 100644 test/syscalls/build_defs.bzl create mode 100644 test/syscalls/gtest/BUILD create mode 100644 test/syscalls/gtest/gtest.go create mode 100644 test/syscalls/linux/32bit.cc create mode 100644 test/syscalls/linux/BUILD create mode 100644 test/syscalls/linux/accept_bind.cc create mode 100644 test/syscalls/linux/accept_bind_stream.cc create mode 100644 test/syscalls/linux/access.cc create mode 100644 test/syscalls/linux/affinity.cc create mode 100644 test/syscalls/linux/aio.cc create mode 100644 test/syscalls/linux/alarm.cc create mode 100644 test/syscalls/linux/arch_prctl.cc create mode 100644 test/syscalls/linux/bad.cc create mode 100644 test/syscalls/linux/base_poll_test.cc create mode 100644 test/syscalls/linux/base_poll_test.h create mode 100644 test/syscalls/linux/bind.cc create mode 100644 test/syscalls/linux/brk.cc create mode 100644 test/syscalls/linux/chdir.cc create mode 100644 test/syscalls/linux/chmod.cc create mode 100644 test/syscalls/linux/chown.cc create mode 100644 test/syscalls/linux/chroot.cc create mode 100644 test/syscalls/linux/clock_getres.cc create mode 100644 test/syscalls/linux/clock_gettime.cc create mode 100644 test/syscalls/linux/clock_nanosleep.cc create mode 100644 test/syscalls/linux/concurrency.cc create mode 100644 test/syscalls/linux/creat.cc create mode 100644 test/syscalls/linux/dev.cc create mode 100644 test/syscalls/linux/dup.cc create mode 100644 test/syscalls/linux/epoll.cc create mode 100644 test/syscalls/linux/eventfd.cc create mode 100644 test/syscalls/linux/exceptions.cc create mode 100644 test/syscalls/linux/exec.cc create mode 100644 test/syscalls/linux/exec.h create mode 100644 test/syscalls/linux/exec_assert_closed_workload.cc create mode 100644 test/syscalls/linux/exec_basic_workload.cc create mode 100644 test/syscalls/linux/exec_binary.cc create mode 100644 test/syscalls/linux/exec_proc_exe_workload.cc create mode 100644 test/syscalls/linux/exec_state_workload.cc create mode 100644 test/syscalls/linux/exit.cc create mode 100755 test/syscalls/linux/exit_script.sh create mode 100644 test/syscalls/linux/fadvise64.cc create mode 100644 test/syscalls/linux/fallocate.cc create mode 100644 test/syscalls/linux/fault.cc create mode 100644 test/syscalls/linux/fchdir.cc create mode 100644 test/syscalls/linux/fcntl.cc create mode 100644 test/syscalls/linux/file_base.h create mode 100644 test/syscalls/linux/flock.cc create mode 100644 test/syscalls/linux/fork.cc create mode 100644 test/syscalls/linux/fpsig_fork.cc create mode 100644 test/syscalls/linux/fpsig_nested.cc create mode 100644 test/syscalls/linux/fsync.cc create mode 100644 test/syscalls/linux/futex.cc create mode 100644 test/syscalls/linux/getcpu.cc create mode 100644 test/syscalls/linux/getdents.cc create mode 100644 test/syscalls/linux/getrandom.cc create mode 100644 test/syscalls/linux/getrusage.cc create mode 100644 test/syscalls/linux/inotify.cc create mode 100644 test/syscalls/linux/ioctl.cc create mode 100644 test/syscalls/linux/ip_socket_test_util.cc create mode 100644 test/syscalls/linux/ip_socket_test_util.h create mode 100644 test/syscalls/linux/itimer.cc create mode 100644 test/syscalls/linux/kill.cc create mode 100644 test/syscalls/linux/link.cc create mode 100644 test/syscalls/linux/lseek.cc create mode 100644 test/syscalls/linux/madvise.cc create mode 100644 test/syscalls/linux/memory_accounting.cc create mode 100644 test/syscalls/linux/mempolicy.cc create mode 100644 test/syscalls/linux/mincore.cc create mode 100644 test/syscalls/linux/mkdir.cc create mode 100644 test/syscalls/linux/mknod.cc create mode 100644 test/syscalls/linux/mmap.cc create mode 100644 test/syscalls/linux/mount.cc create mode 100644 test/syscalls/linux/mremap.cc create mode 100644 test/syscalls/linux/msync.cc create mode 100644 test/syscalls/linux/munmap.cc create mode 100644 test/syscalls/linux/open.cc create mode 100644 test/syscalls/linux/open_create.cc create mode 100644 test/syscalls/linux/partial_bad_buffer.cc create mode 100644 test/syscalls/linux/pause.cc create mode 100644 test/syscalls/linux/pipe.cc create mode 100644 test/syscalls/linux/poll.cc create mode 100644 test/syscalls/linux/ppoll.cc create mode 100644 test/syscalls/linux/prctl.cc create mode 100644 test/syscalls/linux/prctl_setuid.cc create mode 100644 test/syscalls/linux/pread64.cc create mode 100644 test/syscalls/linux/preadv.cc create mode 100644 test/syscalls/linux/preadv2.cc create mode 100644 test/syscalls/linux/priority.cc create mode 100644 test/syscalls/linux/priority_execve.cc create mode 100644 test/syscalls/linux/proc.cc create mode 100644 test/syscalls/linux/proc_net.cc create mode 100644 test/syscalls/linux/pselect.cc create mode 100644 test/syscalls/linux/ptrace.cc create mode 100644 test/syscalls/linux/pty.cc create mode 100644 test/syscalls/linux/pwrite64.cc create mode 100644 test/syscalls/linux/read.cc create mode 100644 test/syscalls/linux/readv.cc create mode 100644 test/syscalls/linux/readv_common.cc create mode 100644 test/syscalls/linux/readv_common.h create mode 100644 test/syscalls/linux/readv_socket.cc create mode 100644 test/syscalls/linux/rename.cc create mode 100644 test/syscalls/linux/rlimits.cc create mode 100644 test/syscalls/linux/rtsignal.cc create mode 100644 test/syscalls/linux/sched.cc create mode 100644 test/syscalls/linux/sched_yield.cc create mode 100644 test/syscalls/linux/seccomp.cc create mode 100644 test/syscalls/linux/select.cc create mode 100644 test/syscalls/linux/semaphore.cc create mode 100644 test/syscalls/linux/sendfile.cc create mode 100644 test/syscalls/linux/sendfile_socket.cc create mode 100644 test/syscalls/linux/shm.cc create mode 100644 test/syscalls/linux/sigaction.cc create mode 100644 test/syscalls/linux/sigaltstack.cc create mode 100644 test/syscalls/linux/sigaltstack_check.cc create mode 100644 test/syscalls/linux/sigiret.cc create mode 100644 test/syscalls/linux/sigprocmask.cc create mode 100644 test/syscalls/linux/sigstop.cc create mode 100644 test/syscalls/linux/sigtimedwait.cc create mode 100644 test/syscalls/linux/socket_abstract.cc create mode 100644 test/syscalls/linux/socket_filesystem.cc create mode 100644 test/syscalls/linux/socket_generic.cc create mode 100644 test/syscalls/linux/socket_generic.h create mode 100644 test/syscalls/linux/socket_inet_loopback.cc create mode 100644 test/syscalls/linux/socket_ip_tcp_generic.cc create mode 100644 test/syscalls/linux/socket_ip_tcp_generic.h create mode 100644 test/syscalls/linux/socket_ip_tcp_generic_loopback.cc create mode 100644 test/syscalls/linux/socket_ip_tcp_loopback.cc create mode 100644 test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc create mode 100644 test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc create mode 100644 test/syscalls/linux/socket_ip_tcp_udp_generic.cc create mode 100644 test/syscalls/linux/socket_ip_udp_loopback.cc create mode 100644 test/syscalls/linux/socket_ip_udp_loopback_blocking.cc create mode 100644 test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc create mode 100644 test/syscalls/linux/socket_netdevice.cc create mode 100644 test/syscalls/linux/socket_netlink_route.cc create mode 100644 test/syscalls/linux/socket_netlink_util.cc create mode 100644 test/syscalls/linux/socket_netlink_util.h create mode 100644 test/syscalls/linux/socket_non_blocking.cc create mode 100644 test/syscalls/linux/socket_non_blocking.h create mode 100644 test/syscalls/linux/socket_non_stream.cc create mode 100644 test/syscalls/linux/socket_non_stream.h create mode 100644 test/syscalls/linux/socket_non_stream_blocking.cc create mode 100644 test/syscalls/linux/socket_non_stream_blocking.h create mode 100644 test/syscalls/linux/socket_stream.cc create mode 100644 test/syscalls/linux/socket_stream.h create mode 100644 test/syscalls/linux/socket_stream_blocking.cc create mode 100644 test/syscalls/linux/socket_stream_blocking.h create mode 100644 test/syscalls/linux/socket_stream_nonblock.cc create mode 100644 test/syscalls/linux/socket_stream_nonblock.h create mode 100644 test/syscalls/linux/socket_test_util.cc create mode 100644 test/syscalls/linux/socket_test_util.h create mode 100644 test/syscalls/linux/socket_unix.cc create mode 100644 test/syscalls/linux/socket_unix.h create mode 100644 test/syscalls/linux/socket_unix_abstract.cc create mode 100644 test/syscalls/linux/socket_unix_abstract_nonblock.cc create mode 100644 test/syscalls/linux/socket_unix_dgram.cc create mode 100644 test/syscalls/linux/socket_unix_dgram.h create mode 100644 test/syscalls/linux/socket_unix_dgram_local.cc create mode 100644 test/syscalls/linux/socket_unix_dgram_non_blocking.cc create mode 100644 test/syscalls/linux/socket_unix_domain.cc create mode 100644 test/syscalls/linux/socket_unix_filesystem.cc create mode 100644 test/syscalls/linux/socket_unix_filesystem_nonblock.cc create mode 100644 test/syscalls/linux/socket_unix_non_stream.cc create mode 100644 test/syscalls/linux/socket_unix_non_stream.h create mode 100644 test/syscalls/linux/socket_unix_non_stream_blocking_local.cc create mode 100644 test/syscalls/linux/socket_unix_pair.cc create mode 100644 test/syscalls/linux/socket_unix_pair_nonblock.cc create mode 100644 test/syscalls/linux/socket_unix_seqpacket.cc create mode 100644 test/syscalls/linux/socket_unix_seqpacket.h create mode 100644 test/syscalls/linux/socket_unix_seqpacket_local.cc create mode 100644 test/syscalls/linux/socket_unix_stream.cc create mode 100644 test/syscalls/linux/socket_unix_stream_blocking_local.cc create mode 100644 test/syscalls/linux/socket_unix_stream_local.cc create mode 100644 test/syscalls/linux/socket_unix_stream_nonblock_local.cc create mode 100644 test/syscalls/linux/socket_unix_unbound_abstract.cc create mode 100644 test/syscalls/linux/socket_unix_unbound_dgram.cc create mode 100644 test/syscalls/linux/socket_unix_unbound_filesystem.cc create mode 100644 test/syscalls/linux/socket_unix_unbound_seqpacket.cc create mode 100644 test/syscalls/linux/socket_unix_unbound_stream.cc create mode 100644 test/syscalls/linux/stat.cc create mode 100644 test/syscalls/linux/stat_times.cc create mode 100644 test/syscalls/linux/statfs.cc create mode 100644 test/syscalls/linux/sticky.cc create mode 100644 test/syscalls/linux/symlink.cc create mode 100644 test/syscalls/linux/sync.cc create mode 100644 test/syscalls/linux/sync_file_range.cc create mode 100644 test/syscalls/linux/sysinfo.cc create mode 100644 test/syscalls/linux/syslog.cc create mode 100644 test/syscalls/linux/sysret.cc create mode 100644 test/syscalls/linux/tcp_socket.cc create mode 100644 test/syscalls/linux/temp_umask.h create mode 100644 test/syscalls/linux/tgkill.cc create mode 100644 test/syscalls/linux/time.cc create mode 100644 test/syscalls/linux/timerfd.cc create mode 100644 test/syscalls/linux/timers.cc create mode 100644 test/syscalls/linux/tkill.cc create mode 100644 test/syscalls/linux/truncate.cc create mode 100644 test/syscalls/linux/udp_bind.cc create mode 100644 test/syscalls/linux/udp_socket.cc create mode 100644 test/syscalls/linux/uidgid.cc create mode 100644 test/syscalls/linux/uname.cc create mode 100644 test/syscalls/linux/unix_domain_socket_test_util.cc create mode 100644 test/syscalls/linux/unix_domain_socket_test_util.h create mode 100644 test/syscalls/linux/unlink.cc create mode 100644 test/syscalls/linux/unshare.cc create mode 100644 test/syscalls/linux/utimes.cc create mode 100644 test/syscalls/linux/vdso.cc create mode 100644 test/syscalls/linux/vdso_clock_gettime.cc create mode 100644 test/syscalls/linux/vfork.cc create mode 100644 test/syscalls/linux/vsyscall.cc create mode 100644 test/syscalls/linux/wait.cc create mode 100644 test/syscalls/linux/write.cc create mode 100644 test/syscalls/syscall_test.go create mode 100755 test/syscalls/syscall_test_runner.sh create mode 100644 test/util/BUILD create mode 100644 test/util/capability_util.cc create mode 100644 test/util/capability_util.h create mode 100644 test/util/cleanup.h create mode 100644 test/util/file_descriptor.h create mode 100644 test/util/fs_util.cc create mode 100644 test/util/fs_util.h create mode 100644 test/util/fs_util_test.cc create mode 100644 test/util/logging.cc create mode 100644 test/util/logging.h create mode 100644 test/util/memory_util.h create mode 100644 test/util/mount_util.h create mode 100644 test/util/multiprocess_util.cc create mode 100644 test/util/multiprocess_util.h create mode 100644 test/util/posix_error.cc create mode 100644 test/util/posix_error.h create mode 100644 test/util/posix_error_test.cc create mode 100644 test/util/proc_util.cc create mode 100644 test/util/proc_util.h create mode 100644 test/util/save_util.cc create mode 100644 test/util/save_util.h create mode 100644 test/util/signal_util.cc create mode 100644 test/util/signal_util.h create mode 100644 test/util/temp_path.cc create mode 100644 test/util/temp_path.h create mode 100644 test/util/test_main.cc create mode 100644 test/util/test_util.cc create mode 100644 test/util/test_util.h create mode 100644 test/util/test_util_test.cc create mode 100644 test/util/thread_util.h create mode 100644 test/util/timer_util.cc create mode 100644 test/util/timer_util.h (limited to 'runsc/specutils') diff --git a/kokoro/run_tests.sh b/kokoro/run_tests.sh index 927acb6a1..ea6440140 100755 --- a/kokoro/run_tests.sh +++ b/kokoro/run_tests.sh @@ -28,8 +28,8 @@ bazel version cd git/repo -# Build everything. -bazel build //... +# Build everything except //test. +bazel build //pkg/... //runsc/... //tools/... # Test use this variable to determine what runtime to use. runtime=runsc_test_$((RANDOM)) @@ -45,7 +45,10 @@ uninstallRuntime() { # We turn off "-e" flag because we must move the log files even if the test # fails. set +e -bazel test --test_output=errors //... + +# Note: We do not run the tests in the //test folder as these would take +# too long. +bazel test --test_output=errors //pkg/... //runsc/... //tools/... exit_code=${?} # This function spawns a subshell to install crictl and containerd. diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index 07afce807..15a7cdae1 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -21,6 +21,7 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/boot", visibility = [ "//runsc:__subpackages__", + "//test:__subpackages__", ], deps = [ "//pkg/abi", diff --git a/runsc/container/BUILD b/runsc/container/BUILD index f57af582a..28ec81d3f 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -13,6 +13,7 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/container", visibility = [ "//runsc:__subpackages__", + "//test:__subpackages__", ], deps = [ "//pkg/log", diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index a1e5da3f5..77a10e2b6 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -11,6 +11,7 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/specutils", visibility = [ "//runsc:__subpackages__", + "//test:__subpackages__", ], deps = [ "//pkg/abi/linux", diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index 3ed235393..826b7bf0b 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -13,6 +13,7 @@ go_library( importpath = "gvisor.googlesource.com/gvisor/runsc/test/testutil", visibility = [ "//runsc:__subpackages__", + "//test:__subpackages__", ], deps = [ "//runsc/boot", diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD new file mode 100644 index 000000000..318d80393 --- /dev/null +++ b/test/syscalls/BUILD @@ -0,0 +1,522 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +package(licenses = ["notice"]) # Apache 2.0 + +load("//test/syscalls:build_defs.bzl", "syscall_test") + +syscall_test(test = "//test/syscalls/linux:32bit_test") + +syscall_test(test = "//test/syscalls/linux:accept_bind_stream_test") + +syscall_test( + size = "enormous", + test = "//test/syscalls/linux:accept_bind_test", +) + +syscall_test(test = "//test/syscalls/linux:access_test") + +syscall_test(test = "//test/syscalls/linux:affinity_test") + +syscall_test(test = "//test/syscalls/linux:aio_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:alarm_test", +) + +syscall_test(test = "//test/syscalls/linux:arch_prctl_test") + +syscall_test(test = "//test/syscalls/linux:bad_test") + +syscall_test( + size = "large", + test = "//test/syscalls/linux:bind_test", +) + +syscall_test(test = "//test/syscalls/linux:brk_test") + +syscall_test(test = "//test/syscalls/linux:chdir_test") + +syscall_test(test = "//test/syscalls/linux:chmod_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:chown_test", +) + +syscall_test(test = "//test/syscalls/linux:chroot_test") + +syscall_test(test = "//test/syscalls/linux:clock_getres_test") + +syscall_test(test = "//test/syscalls/linux:clock_gettime_test") + +syscall_test(test = "//test/syscalls/linux:clock_nanosleep_test") + +syscall_test(test = "//test/syscalls/linux:concurrency_test") + +syscall_test(test = "//test/syscalls/linux:creat_test") + +syscall_test(test = "//test/syscalls/linux:dev_test") + +syscall_test(test = "//test/syscalls/linux:dup_test") + +syscall_test(test = "//test/syscalls/linux:epoll_test") + +syscall_test(test = "//test/syscalls/linux:eventfd_test") + +syscall_test(test = "//test/syscalls/linux:exceptions_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:exec_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:exec_binary_test", +) + +syscall_test(test = "//test/syscalls/linux:exit_test") + +syscall_test(test = "//test/syscalls/linux:fadvise64_test") + +syscall_test(test = "//test/syscalls/linux:fallocate_test") + +syscall_test(test = "//test/syscalls/linux:fault_test") + +syscall_test(test = "//test/syscalls/linux:fchdir_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:fcntl_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:flock_test", +) + +syscall_test(test = "//test/syscalls/linux:fork_test") + +syscall_test(test = "//test/syscalls/linux:fpsig_fork_test") + +syscall_test(test = "//test/syscalls/linux:fpsig_nested_test") + +syscall_test(test = "//test/syscalls/linux:fsync_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:futex_test", +) + +syscall_test(test = "//test/syscalls/linux:getcpu_host_test") + +syscall_test(test = "//test/syscalls/linux:getcpu_test") + +syscall_test(test = "//test/syscalls/linux:getdents_test") + +syscall_test(test = "//test/syscalls/linux:getrandom_test") + +syscall_test(test = "//test/syscalls/linux:getrusage_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:inotify_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:ioctl_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:itimer_test", +) + +syscall_test(test = "//test/syscalls/linux:kill_test") + +syscall_test(test = "//test/syscalls/linux:link_test") + +syscall_test(test = "//test/syscalls/linux:lseek_test") + +syscall_test(test = "//test/syscalls/linux:madvise_test") + +syscall_test(test = "//test/syscalls/linux:memory_accounting_test") + +syscall_test(test = "//test/syscalls/linux:mempolicy_test") + +syscall_test(test = "//test/syscalls/linux:mincore_test") + +syscall_test(test = "//test/syscalls/linux:mkdir_test") + +syscall_test(test = "//test/syscalls/linux:mknod_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:mmap_test", +) + +syscall_test(test = "//test/syscalls/linux:mount_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:mremap_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:msync_test", +) + +syscall_test(test = "//test/syscalls/linux:munmap_test") + +syscall_test(test = "//test/syscalls/linux:open_create_test") + +syscall_test(test = "//test/syscalls/linux:open_test") + +syscall_test(test = "//test/syscalls/linux:partial_bad_buffer_test") + +syscall_test(test = "//test/syscalls/linux:pause_test") + +syscall_test(test = "//test/syscalls/linux:pipe_test") + +syscall_test(test = "//test/syscalls/linux:poll_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:ppoll_test", +) + +syscall_test(test = "//test/syscalls/linux:prctl_setuid_test") + +syscall_test(test = "//test/syscalls/linux:prctl_test") + +syscall_test(test = "//test/syscalls/linux:pread64_test") + +syscall_test(test = "//test/syscalls/linux:preadv_test") + +syscall_test(test = "//test/syscalls/linux:preadv2_test") + +syscall_test(test = "//test/syscalls/linux:priority_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:proc_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:pselect_test", +) + +syscall_test(test = "//test/syscalls/linux:ptrace_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:pty_test", +) + +syscall_test(test = "//test/syscalls/linux:pwrite64_test") + +syscall_test(test = "//test/syscalls/linux:read_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:readv_socket_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:readv_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:rename_test", +) + +syscall_test(test = "//test/syscalls/linux:rlimits_test") + +syscall_test(test = "//test/syscalls/linux:rtsignal_test") + +syscall_test(test = "//test/syscalls/linux:sched_test") + +syscall_test(test = "//test/syscalls/linux:sched_yield_test") + +syscall_test(test = "//test/syscalls/linux:seccomp_test") + +syscall_test(test = "//test/syscalls/linux:select_test") + +syscall_test(test = "//test/syscalls/linux:semaphore_test") + +syscall_test(test = "//test/syscalls/linux:sendfile_socket_test") + +syscall_test(test = "//test/syscalls/linux:sendfile_test") + +syscall_test(test = "//test/syscalls/linux:sigaction_test") + +# TODO: Enable once the test passes in runsc. +# syscall_test(test = "//test/syscalls/linux:sigaltstack_test") + +syscall_test(test = "//test/syscalls/linux:sigiret_test") + +syscall_test(test = "//test/syscalls/linux:sigprocmask_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:sigstop_test", +) + +syscall_test(test = "//test/syscalls/linux:sigtimedwait_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:shm_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_abstract_non_blocking_test", +) + +syscall_test( + size = "enormous", + test = "//test/syscalls/linux:socket_abstract_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_domain_non_blocking_test", +) + +syscall_test( + size = "enormous", + test = "//test/syscalls/linux:socket_domain_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_filesystem_non_blocking_test", +) + +syscall_test( + size = "enormous", + test = "//test/syscalls/linux:socket_filesystem_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_inet_loopback_test", +) + +syscall_test( + size = "large", + test = "//test/syscalls/linux:socket_ip_tcp_generic_loopback_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_ip_tcp_loopback_non_blocking_test", +) + +syscall_test( + size = "large", + test = "//test/syscalls/linux:socket_ip_tcp_loopback_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_ip_tcp_udp_generic_loopback_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_ip_udp_loopback_non_blocking_test", +) + +syscall_test( + size = "large", + test = "//test/syscalls/linux:socket_ip_udp_loopback_test", +) + +syscall_test(test = "//test/syscalls/linux:socket_netdevice_test") + +syscall_test(test = "//test/syscalls/linux:socket_netlink_route_test") + +syscall_test(test = "//test/syscalls/linux:socket_non_stream_blocking_local_test") + +syscall_test(test = "//test/syscalls/linux:socket_non_stream_blocking_udp_test") + +syscall_test( + size = "large", + test = "//test/syscalls/linux:socket_stream_blocking_local_test", +) + +syscall_test( + size = "large", + test = "//test/syscalls/linux:socket_stream_blocking_tcp_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_stream_local_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_stream_nonblock_local_test", +) + +syscall_test( + size = "enormous", + test = "//test/syscalls/linux:socket_unix_abstract_test", +) + +syscall_test( + # NOTE: Large sendmsg may stall a long time. + size = "enormous", + test = "//test/syscalls/linux:socket_unix_dgram_local_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_unix_dgram_non_blocking_test", +) + +syscall_test( + size = "enormous", + test = "//test/syscalls/linux:socket_unix_filesystem_test", +) + +syscall_test( + size = "enormous", + test = "//test/syscalls/linux:socket_unix_pair_test", +) + +syscall_test( + # NOTE: Large sendmsg may stall a long time. + size = "enormous", + test = "//test/syscalls/linux:socket_unix_seqpacket_local_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_unix_stream_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_unix_unbound_abstract_test", +) + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_unix_unbound_dgram_test", +) + +syscall_test(test = "//test/syscalls/linux:socket_unix_unbound_filesystem_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:socket_unix_unbound_seqpacket_test", +) + +syscall_test( + size = "large", + test = "//test/syscalls/linux:socket_unix_unbound_stream_test", +) + +syscall_test(test = "//test/syscalls/linux:statfs_test") + +syscall_test(test = "//test/syscalls/linux:stat_test") + +syscall_test(test = "//test/syscalls/linux:stat_times_test") + +syscall_test(test = "//test/syscalls/linux:sticky_test") + +syscall_test(test = "//test/syscalls/linux:symlink_test") + +syscall_test(test = "//test/syscalls/linux:sync_test") + +syscall_test(test = "//test/syscalls/linux:sync_file_range_test") + +syscall_test(test = "//test/syscalls/linux:sysinfo_test") + +syscall_test(test = "//test/syscalls/linux:syslog_test") + +syscall_test(test = "//test/syscalls/linux:sysret_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:tcp_socket_test", +) + +syscall_test(test = "//test/syscalls/linux:tgkill_test") + +syscall_test(test = "//test/syscalls/linux:timerfd_test") + +syscall_test(test = "//test/syscalls/linux:timers_test") + +syscall_test(test = "//test/syscalls/linux:time_test") + +syscall_test(test = "//test/syscalls/linux:tkill_test") + +syscall_test(test = "//test/syscalls/linux:truncate_test") + +syscall_test(test = "//test/syscalls/linux:udp_bind_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:udp_socket_test", +) + +syscall_test(test = "//test/syscalls/linux:uidgid_test") + +syscall_test(test = "//test/syscalls/linux:uname_test") + +syscall_test(test = "//test/syscalls/linux:unlink_test") + +syscall_test(test = "//test/syscalls/linux:unshare_test") + +syscall_test(test = "//test/syscalls/linux:utimes_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:vdso_clock_gettime_test", +) + +syscall_test(test = "//test/syscalls/linux:vdso_test") + +syscall_test(test = "//test/syscalls/linux:vsyscall_test") + +syscall_test(test = "//test/syscalls/linux:vfork_test") + +syscall_test( + size = "medium", + test = "//test/syscalls/linux:wait_test", +) + +syscall_test(test = "//test/syscalls/linux:write_test") + +go_test( + name = "syscall_test", + srcs = ["syscall_test.go"], + data = [ + "//runsc", + ], + # Running this test by itself does not make sense. It should only be run + # via the syscall_test macro. + tags = [ + "manual", + ], + deps = [ + "//pkg/log", + "//runsc/boot", + "//runsc/container", + "//runsc/specutils", + "//runsc/test/testutil", + "//test/syscalls/gtest", + "@org_golang_x_sys//unix:go_default_library", + ], +) diff --git a/test/syscalls/README.md b/test/syscalls/README.md new file mode 100644 index 000000000..69712663c --- /dev/null +++ b/test/syscalls/README.md @@ -0,0 +1,110 @@ +# gVisor system call test suite + +This is a test suite for Linux system calls. It runs under both gVisor and +Linux, and ensures compatability between the two. + +When adding support for a new syscall (or syscall argument) to gVisor, a +corresponding syscall test should be added. It's usually recommended to write +the test first and make sure that it passes on Linux before making changes to +gVisor. + +This document outlines the general guidelines for tests and specific rules that +must be followed for new tests. + +## Running the tests + +Each test file generates three different test targets that run in different +environments: + +* a `native` target that runs directly on the host machine, +* a `runsc_ptrace` target that runs inside runsc using the ptrace platform, and +* a `runsc_kvm` target that runs inside runsc using the KVM platform. + +For example, the test in `access_test.cc` generates the following targets: + +* `//test/syscalls:access_test_native` +* `//test/syscalls:access_test_runsc_ptrace` +* `//test/syscalls:access_test_runsc_kvm` + +Any of these targets can be run directly via `bazel test`. + +```bash +$ bazel test //test/syscalls:access_test_native +$ bazel test //test/syscalls:access_test_runsc_ptrace +$ bazel test //test/syscalls:access_test_runsc_kvm +``` + +To run all the tests on a particular platform, you can filter by the platform +tag: + +```bash +# Run all tests in native environment: +$ bazel test --test_tag_filter=native //test/syscalls:* + +# Run all tests in runsc with ptrace: +$ bazel test --test_tag_filter=runsc_ptrace //test/syscalls:* + +# Run all tests in runsc with kvm: +$ bazel test --test_tag_filter=runsc_kvm //test/syscalls:* +``` + +You can also run all the tests on every platform. (Warning, this may take a +while to run.) + +```bash +# Run all tests on every platform: +$ bazel test //test/syscalls:* +``` + +## Writing new tests + +Whenever we add support for a new syscall, or add support for a new argument or +option for a syscall, we should always add a new test (perhaps many new tests). + +In general, it is best to write the test first and make sure it passes on Linux +by running the test on the `native` platform on a Linux machine. This ensures +that the gVisor implementation matches actual Linux behavior. Sometimes man +pages contain errors, so always check the actual Linux behavior. + +gVisor uses the [Google Test][googletest] test framework, with a few custom +matchers and guidelines, described below. + +### Syscall matchers + +When testing an individual system call, use the following syscall matchers, +which will match the value returned by the syscall and the errno. + +```cc +SyscallSucceeds() +SyscallSucceedsWithValue(...) +SyscallFails() +SyscallFailsWithErrno(...) +``` + +### Use test utilities (RAII classes) + +The test utilties are written as RAII classes. These utilities should be +preferred over custom test harnesses. + +Local class instances should be preferred, whereever possible, over full test +fixtures. + +A test utility should be created when there is more than one test that requires +that same functionality, otherwise the class should be test local. + + +## Save/Restore support in tests +gVisor supports save/restore, and our syscall tests are written in a way to +enable saving/restoring at certain points. Hence, there are calls to +`MaybeSave`, and certain tests that should not trigger saves are named with +`NoSave`. + +However, the current open-source test runner does not yet support triggering +save/restore, so these functions and annotations have no effect on the +open-source tests. + +We plan on extending our open-source test runner to trigger save/restore. Until +then, these functions and annotations should be ignored. + + +[googletest]: https://github.com/abseil/googletest diff --git a/test/syscalls/build_defs.bzl b/test/syscalls/build_defs.bzl new file mode 100644 index 000000000..31b311f63 --- /dev/null +++ b/test/syscalls/build_defs.bzl @@ -0,0 +1,54 @@ +"""Defines a rule for syscall test targets.""" + +# syscall_test is a macro that will create targets to run the given test target +# on the host (native) and runsc. +def syscall_test(test, size = "small"): + _syscall_test(test, size, "native") + _syscall_test(test, size, "kvm") + _syscall_test(test, size, "ptrace") + +def _syscall_test(test, size, platform): + test_name = test.split(":")[1] + + # Prepend "runsc" to non-native platform names. + full_platform = platform if platform == "native" else "runsc_" + platform + + # Add the full_platform in a tag to make it easier to run all the tests on + # a specific platform. + tags = [full_platform] + + # Add tag to prevent the tests from running in a Bazel sandbox. + # TODO: Make the tests run without this tag. + tags.append("no-sandbox") + + # TODO: KVM tests are tagged "manual" to until the platform is + # more stable. + if platform == "kvm": + tags += ["manual"] + + sh_test( + srcs = ["syscall_test_runner.sh"], + name = test_name + "_" + full_platform, + data = [ + ":syscall_test", + test, + ], + args = [ + # First argument is location to syscall_test binary. + "$(location :syscall_test)", + # Rest of arguments are passed directly to syscall_test binary. + "--test-name=" + test_name, + "--platform=" + platform, + "--debug=false", + "--strace=false", + "--parallel=true", + ], + size = size, + tags = tags, + ) + +def sh_test(**kwargs): + """Wraps the standard sh_test.""" + native.sh_test( + **kwargs + ) diff --git a/test/syscalls/gtest/BUILD b/test/syscalls/gtest/BUILD new file mode 100644 index 000000000..d078fd3d5 --- /dev/null +++ b/test/syscalls/gtest/BUILD @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +package(licenses = ["notice"]) # Apache 2.0 + +go_library( + name = "gtest", + srcs = ["gtest.go"], + importpath = "gvisor.googlesource.com/gvisor/test/syscalls/gtest", + visibility = [ + "//test:__subpackages__", + ], +) diff --git a/test/syscalls/gtest/gtest.go b/test/syscalls/gtest/gtest.go new file mode 100644 index 000000000..dfe5037cd --- /dev/null +++ b/test/syscalls/gtest/gtest.go @@ -0,0 +1,93 @@ +// Copyright 2018 Google LLC +// +// 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 gtest contains helpers for running google-test tests from Go. +package gtest + +import ( + "fmt" + "os/exec" + "strings" +) + +var ( + // ListTestFlag is the flag that will list tests in gtest binaries. + ListTestFlag = "--gtest_list_tests" + + // FilterTestFlag is the flag that will filter tests in gtest binaries. + FilterTestFlag = "--gtest_filter" +) + +// TestCase is a single gtest test case. +type TestCase struct { + // Suite is the suite for this test. + Suite string + + // Name is the name of this individual test. + Name string +} + +// FullName returns the name of the test including the suite. It is suitable to +// pass to "-gtest_filter". +func (tc TestCase) FullName() string { + return fmt.Sprintf("%s.%s", tc.Suite, tc.Name) +} + +// ParseTestCases calls a gtest test binary to list its test and returns a +// slice with the name and suite of each test. +func ParseTestCases(testBin string, extraArgs ...string) ([]TestCase, error) { + args := append([]string{ListTestFlag}, extraArgs...) + cmd := exec.Command(testBin, args...) + out, err := cmd.Output() + if err != nil { + exitErr, ok := err.(*exec.ExitError) + if !ok { + return nil, fmt.Errorf("could not enumerate gtest tests: %v", err) + } + return nil, fmt.Errorf("could not enumerate gtest tests: %v\nstderr:\n%s", err, exitErr.Stderr) + } + + var t []TestCase + var suite string + for _, line := range strings.Split(string(out), "\n") { + // Strip comments. + line = strings.Split(line, "#")[0] + + // New suite? + if !strings.HasPrefix(line, " ") { + suite = strings.TrimSuffix(strings.TrimSpace(line), ".") + continue + } + + // Individual test. + name := strings.TrimSpace(line) + + // Do we have a suite yet? + if suite == "" { + return nil, fmt.Errorf("test without a suite: %v", name) + } + + // Add this individual test. + t = append(t, TestCase{ + Suite: suite, + Name: name, + }) + + } + + if len(t) == 0 { + return nil, fmt.Errorf("no tests parsed from %v", testBin) + } + return t, nil +} diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc new file mode 100644 index 000000000..b8d5f0355 --- /dev/null +++ b/test/syscalls/linux/32bit.cc @@ -0,0 +1,226 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "gtest/gtest.h" + +#ifndef __x86_64__ +#error "This test is x86-64 specific." +#endif + +namespace gvisor { +namespace testing { + +namespace { + +constexpr char kInt3 = '\xcc'; + +constexpr char kInt80[2] = {'\xcd', '\x80'}; +constexpr char kSyscall[2] = {'\x0f', '\x05'}; +constexpr char kSysenter[2] = {'\x0f', '\x34'}; + +void ExitGroup32(const char instruction[2], int code) { + const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( + Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0)); + + // Fill with INT 3 in case we execute too far. + memset(m.ptr(), kInt3, m.len()); + + memcpy(m.ptr(), instruction, 2); + + // We're playing *extremely* fast-and-loose with the various syscall ABIs + // here, which we can more-or-less get away with since exit_group doesn't + // return. + // + // SYSENTER expects the user stack in (%ebp) and arg6 in 0(%ebp). The kernel + // will unconditionally dereference %ebp for arg6, so we must pass a valid + // address or it will return EFAULT. + // + // SYSENTER also unconditionally returns to thread_info->sysenter_return which + // is ostensibly a stub in the 32-bit VDSO. But a 64-bit binary doesn't have + // the 32-bit VDSO mapped, so sysenter_return will simply be the value + // inherited from the most recent 32-bit ancestor, or NULL if there is none. + // As a result, return would not return from SYSENTER. + asm volatile( + "movl $252, %%eax\n" // exit_group + "movl %[code], %%ebx\n" // code + "movl %%edx, %%ebp\n" // SYSENTER: user stack (use IP as a valid addr) + "leaq -20(%%rsp), %%rsp\n" + "movl $0x2b, 16(%%rsp)\n" // SS = CPL3 data segment + "movl $0,12(%%rsp)\n" // ESP = nullptr (unused) + "movl $0, 8(%%rsp)\n" // EFLAGS + "movl $0x23, 4(%%rsp)\n" // CS = CPL3 32-bit code segment + "movl %%edx, 0(%%rsp)\n" // EIP + "iretl\n" + "int $3\n" + : + : [code] "m"(code), [ip] "d"(m.ptr()) + : "rax", "rbx", "rsp"); +} + +constexpr int kExitCode = 42; + +TEST(Syscall32Bit, Int80) { + switch (GvisorPlatform()) { + case Platform::kKVM: + // TODO: 32-bit segments are broken (but not explictly + // disabled). + return; + case Platform::kPtrace: + // TODO: The ptrace platform does not have a consistent story + // here. + return; + case Platform::kNative: + break; + } + + // Upstream Linux. 32-bit syscalls allowed. + EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42), + ""); +} + +TEST(Syscall32Bit, Sysenter) { + switch (GvisorPlatform()) { + case Platform::kKVM: + // TODO: See above. + return; + case Platform::kPtrace: + // TODO: See above. + return; + case Platform::kNative: + break; + } + + if (GetCPUVendor() == CPUVendor::kAMD) { + // SYSENTER is an illegal instruction in compatibility mode on AMD. + EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), + ::testing::KilledBySignal(SIGILL), ""); + return; + } + + // Upstream Linux on !AMD, 32-bit syscalls allowed. + EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), ::testing::ExitedWithCode(42), + ""); +} + +TEST(Syscall32Bit, Syscall) { + switch (GvisorPlatform()) { + case Platform::kKVM: + // TODO: See above. + return; + case Platform::kPtrace: + // TODO: See above. + return; + case Platform::kNative: + break; + } + + if (GetCPUVendor() == CPUVendor::kIntel) { + // SYSCALL is an illegal instruction in compatibility mode on Intel. + EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), + ::testing::KilledBySignal(SIGILL), ""); + return; + } + + // Upstream Linux on !Intel, 32-bit syscalls allowed. + EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), ::testing::ExitedWithCode(42), + ""); +} + +// Far call code called below. +// +// Input stack layout: +// +// %esp+12 lcall segment +// %esp+8 lcall address offset +// %esp+0 return address +// +// The lcall will enter compatibility mode and jump to the call address (the +// address of the lret). The lret will return to 64-bit mode at the retq, which +// will return to the external caller of this function. +// +// Since this enters compatibility mode, it must be mapped in a 32-bit region of +// address space and have a 32-bit stack pointer. +constexpr char kFarCall[] = { + '\x67', '\xff', '\x5c', '\x24', '\x08', // lcall *8(%esp) + '\xc3', // retq + '\xcb', // lret +}; + +void FarCall32() { + const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( + Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0)); + + // Fill with INT 3 in case we execute too far. + memset(m.ptr(), kInt3, m.len()); + + // 32-bit code. + memcpy(m.ptr(), kFarCall, sizeof(kFarCall)); + + // Use the end of the code page as its stack. + uintptr_t stack = m.endaddr(); + + uintptr_t lcall = m.addr(); + uintptr_t lret = m.addr() + sizeof(kFarCall) - 1; + + // N.B. We must save and restore RSP manually. GCC can do so automatically + // with an "rsp" clobber, but clang cannot. + asm volatile( + // Place the address of lret (%edx) and the 32-bit code segment (0x23) on + // the 32-bit stack for lcall. + "subl $0x8, %%ecx\n" + "movl $0x23, 4(%%ecx)\n" + "movl %%edx, 0(%%ecx)\n" + + // Save the current stack and switch to 32-bit stack. + "pushq %%rbp\n" + "movq %%rsp, %%rbp\n" + "movq %%rcx, %%rsp\n" + + // Run the lcall code. + "callq *%%rbx\n" + + // Restore the old stack. + "leaveq\n" + : "+c"(stack) + : "b"(lcall), "d"(lret)); +} + +TEST(Call32Bit, Disallowed) { + switch (GvisorPlatform()) { + case Platform::kKVM: + // TODO: See above. + return; + case Platform::kPtrace: + // The ptrace platform cannot prevent switching to compatibility mode. + ABSL_FALLTHROUGH_INTENDED; + case Platform::kNative: + break; + } + + // Shouldn't crash. + FarCall32(); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD new file mode 100644 index 000000000..1c48a2a43 --- /dev/null +++ b/test/syscalls/linux/BUILD @@ -0,0 +1,2951 @@ +package( + default_visibility = ["//:sandbox"], + licenses = ["notice"], # Apache 2.0 +) + +cc_binary( + name = "sigaltstack_check", + testonly = 1, + srcs = ["sigaltstack_check.cc"], + deps = ["//test/util:logging"], +) + +cc_binary( + name = "exec_assert_closed_workload", + testonly = 1, + srcs = ["exec_assert_closed_workload.cc"], + deps = [ + "@com_google_absl//absl/strings", + ], +) + +cc_binary( + name = "exec_basic_workload", + testonly = 1, + srcs = [ + "exec.h", + "exec_basic_workload.cc", + ], +) + +cc_binary( + name = "exec_proc_exe_workload", + testonly = 1, + srcs = ["exec_proc_exe_workload.cc"], + deps = [ + "//test/util:fs_util", + "//test/util:posix_error", + ], +) + +cc_binary( + name = "exec_state_workload", + testonly = 1, + srcs = ["exec_state_workload.cc"], +) + +sh_binary( + name = "exit_script", + testonly = 1, + srcs = [ + "exit_script.sh", + ], +) + +cc_binary( + name = "priority_execve", + testonly = 1, + srcs = [ + "priority_execve.cc", + ], +) + +cc_library( + name = "base_poll_test", + testonly = 1, + srcs = ["base_poll_test.cc"], + hdrs = ["base_poll_test.h"], + deps = [ + "//test/util:logging", + "//test/util:signal_util", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "file_base", + testonly = 1, + hdrs = ["file_base.h"], + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "socket_netlink_util", + testonly = 1, + srcs = ["socket_netlink_util.cc"], + hdrs = ["socket_netlink_util.h"], + deps = [ + ":socket_test_util", + "//test/util:file_descriptor", + "//test/util:posix_error", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "socket_test_util", + testonly = 1, + srcs = ["socket_test_util.cc"], + hdrs = ["socket_test_util.h"], + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "temp_umask", + hdrs = ["temp_umask.h"], +) + +cc_library( + name = "unix_domain_socket_test_util", + testonly = 1, + srcs = ["unix_domain_socket_test_util.cc"], + hdrs = ["unix_domain_socket_test_util.h"], + deps = [ + ":socket_test_util", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "ip_socket_test_util", + testonly = 1, + srcs = ["ip_socket_test_util.cc"], + hdrs = ["ip_socket_test_util.h"], + deps = [ + ":socket_test_util", + ], +) + +cc_binary( + name = "clock_nanosleep_test", + testonly = 1, + srcs = ["clock_nanosleep.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:posix_error", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "32bit_test", + testonly = 1, + srcs = ["32bit.cc"], + linkstatic = 1, + deps = [ + "//test/util:memory_util", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "accept_bind_test", + testonly = 1, + srcs = ["accept_bind.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "accept_bind_stream_test", + testonly = 1, + srcs = ["accept_bind_stream.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "access_test", + testonly = 1, + srcs = ["access.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "affinity_test", + testonly = 1, + srcs = ["affinity.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "aio_test", + testonly = 1, + srcs = [ + "aio.cc", + "file_base.h", + ], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "alarm_test", + testonly = 1, + srcs = ["alarm.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:logging", + "//test/util:signal_util", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "bad_test", + testonly = 1, + srcs = ["bad.cc"], + linkstatic = 1, + visibility = [ + "//:sandbox", + ], + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "bind_test", + testonly = 1, + srcs = ["bind.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "brk_test", + testonly = 1, + srcs = ["brk.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "chdir_test", + testonly = 1, + srcs = ["chdir.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "chmod_test", + testonly = 1, + srcs = ["chmod.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "chown_test", + testonly = 1, + srcs = ["chown.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/synchronization", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sticky_test", + testonly = 1, + srcs = ["sticky.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "chroot_test", + testonly = 1, + srcs = ["chroot.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:mount_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "clock_getres_test", + testonly = 1, + srcs = ["clock_getres.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "clock_gettime_test", + testonly = 1, + srcs = ["clock_gettime.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "concurrency_test", + testonly = 1, + srcs = ["concurrency.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "creat_test", + testonly = 1, + srcs = ["creat.cc"], + linkstatic = 1, + deps = [ + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "dev_test", + testonly = 1, + srcs = ["dev.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "dup_test", + testonly = 1, + srcs = ["dup.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "epoll_test", + testonly = 1, + srcs = ["epoll.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "eventfd_test", + testonly = 1, + srcs = ["eventfd.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "exceptions_test", + testonly = 1, + srcs = ["exceptions.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "getcpu_test", + testonly = 1, + srcs = ["getcpu.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "getcpu_host_test", + testonly = 1, + srcs = ["getcpu.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "getrusage_test", + testonly = 1, + srcs = ["getrusage.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:memory_util", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "exec_binary_test", + testonly = 1, + srcs = ["exec_binary.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:proc_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "exec_test", + testonly = 1, + srcs = [ + "exec.cc", + "exec.h", + ], + data = [ + ":exec_assert_closed_workload", + ":exec_basic_workload", + ":exec_proc_exe_workload", + ":exec_state_workload", + ":exit_script", + ":priority_execve", + ], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "exit_test", + testonly = 1, + srcs = ["exit.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fallocate_test", + testonly = 1, + srcs = ["fallocate.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fault_test", + testonly = 1, + srcs = ["fault.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fchdir_test", + testonly = 1, + srcs = ["fchdir.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fcntl_test", + testonly = 1, + srcs = ["fcntl.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + "//test/util:cleanup", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_util", + "//test/util:timer_util", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "flock_test", + testonly = 1, + srcs = [ + "file_base.h", + "flock.cc", + ], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fork_test", + testonly = 1, + srcs = ["fork.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fpsig_fork_test", + testonly = 1, + srcs = ["fpsig_fork.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fpsig_nested_test", + testonly = 1, + srcs = ["fpsig_nested.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sync_file_range_test", + testonly = 1, + srcs = ["sync_file_range.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "fsync_test", + testonly = 1, + srcs = ["fsync.cc"], + linkstatic = 1, + deps = [ + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "futex_test", + testonly = 1, + srcs = ["futex.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:memory_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "getdents_test", + testonly = 1, + srcs = ["getdents.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "getrandom_test", + testonly = 1, + srcs = ["getrandom.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "inotify_test", + testonly = 1, + srcs = ["inotify.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + ], +) + +cc_binary( + name = "ioctl_test", + testonly = 1, + srcs = ["ioctl.cc"], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:file_descriptor", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "itimer_test", + testonly = 1, + srcs = ["itimer.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:logging", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:signal_util", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "kill_test", + testonly = 1, + srcs = ["kill.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:logging", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "link_test", + testonly = 1, + srcs = ["link.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "lseek_test", + testonly = 1, + srcs = ["lseek.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "madvise_test", + testonly = 1, + srcs = ["madvise.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:logging", + "//test/util:memory_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "mempolicy_test", + testonly = 1, + srcs = ["mempolicy.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/memory", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "mincore_test", + testonly = 1, + srcs = ["mincore.cc"], + linkstatic = 1, + deps = [ + "//test/util:memory_util", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "mkdir_test", + testonly = 1, + srcs = ["mkdir.cc"], + linkstatic = 1, + deps = [ + ":temp_umask", + "//test/util:capability_util", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "mknod_test", + testonly = 1, + srcs = ["mknod.cc"], + linkstatic = 1, + deps = [ + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "mmap_test", + testonly = 1, + srcs = ["mmap.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:memory_util", + "//test/util:multiprocess_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "mount_test", + testonly = 1, + srcs = ["mount.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:mount_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "mremap_test", + testonly = 1, + srcs = ["mremap.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:logging", + "//test/util:memory_util", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "msync_test", + testonly = 1, + srcs = ["msync.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:memory_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "munmap_test", + testonly = 1, + srcs = ["munmap.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "open_test", + testonly = 1, + srcs = [ + "file_base.h", + "open.cc", + ], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "open_create_test", + testonly = 1, + srcs = ["open_create.cc"], + linkstatic = 1, + deps = [ + ":temp_umask", + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "pty_test", + testonly = 1, + srcs = ["pty.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "partial_bad_buffer_test", + testonly = 1, + srcs = ["partial_bad_buffer.cc"], + linkstatic = 1, + deps = [ + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "pause_test", + testonly = 1, + srcs = ["pause.cc"], + linkstatic = 1, + deps = [ + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "pipe_test", + testonly = 1, + srcs = ["pipe.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "poll_test", + testonly = 1, + srcs = ["poll.cc"], + linkstatic = 1, + deps = [ + ":base_poll_test", + "//test/util:file_descriptor", + "//test/util:logging", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "ppoll_test", + testonly = 1, + srcs = ["ppoll.cc"], + linkstatic = 1, + deps = [ + ":base_poll_test", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "arch_prctl_test", + testonly = 1, + srcs = ["arch_prctl.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "prctl_test", + testonly = 1, + srcs = ["prctl.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "prctl_setuid_test", + testonly = 1, + srcs = ["prctl_setuid.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:logging", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "pread64_test", + testonly = 1, + srcs = ["pread64.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "preadv_test", + testonly = 1, + srcs = ["preadv.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:logging", + "//test/util:memory_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "preadv2_test", + testonly = 1, + srcs = [ + "preadv2.cc", + "readv_common.cc", + "readv_common.h", + ], + linkstatic = 1, + deps = [ + ":file_base", + "//test/util:file_descriptor", + "//test/util:memory_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "priority_test", + testonly = 1, + srcs = ["priority.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:fs_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "proc_test", + testonly = 1, + srcs = ["proc.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:memory_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "proc_net_test", + testonly = 1, + srcs = ["proc_net.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "pselect_test", + testonly = 1, + srcs = ["pselect.cc"], + linkstatic = 1, + deps = [ + ":base_poll_test", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "ptrace_test", + testonly = 1, + srcs = ["ptrace.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:multiprocess_util", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "pwrite64_test", + testonly = 1, + srcs = ["pwrite64.cc"], + linkstatic = 1, + deps = [ + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "read_test", + testonly = 1, + srcs = ["read.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "readv_test", + testonly = 1, + srcs = [ + "file_base.h", + "readv.cc", + "readv_common.cc", + "readv_common.h", + ], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:timer_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "readv_socket_test", + testonly = 1, + srcs = [ + "file_base.h", + "readv_common.cc", + "readv_common.h", + "readv_socket.cc", + ], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "rename_test", + testonly = 1, + srcs = ["rename.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "rlimits_test", + testonly = 1, + srcs = ["rlimits.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "rtsignal_test", + testonly = 1, + srcs = ["rtsignal.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:logging", + "//test/util:posix_error", + "//test/util:signal_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sched_test", + testonly = 1, + srcs = ["sched.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sched_yield_test", + testonly = 1, + srcs = ["sched_yield.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "seccomp_test", + testonly = 1, + srcs = ["seccomp.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:memory_util", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:proc_util", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/base:core_headers", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "select_test", + testonly = 1, + srcs = ["select.cc"], + linkstatic = 1, + deps = [ + ":base_poll_test", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sendfile_test", + testonly = 1, + srcs = ["sendfile.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sendfile_socket_test", + testonly = 1, + srcs = ["sendfile_socket.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sigaction_test", + testonly = 1, + srcs = ["sigaction.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sigaltstack_test", + testonly = 1, + srcs = ["sigaltstack.cc"], + data = [ + ":sigaltstack_check", + ], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:fs_util", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sigiret_test", + testonly = 1, + srcs = ["sigiret.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:signal_util", + "//test/util:test_util", + "//test/util:timer_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sigprocmask_test", + testonly = 1, + srcs = ["sigprocmask.cc"], + linkstatic = 1, + deps = [ + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sigstop_test", + testonly = 1, + srcs = ["sigstop.cc"], + linkstatic = 1, + deps = [ + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sigtimedwait_test", + testonly = 1, + srcs = ["sigtimedwait.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:signal_util", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "socket_generic_test_cases", + testonly = 1, + srcs = [ + "socket_generic.cc", + ], + hdrs = [ + "socket_generic.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_unix_dgram_test_cases", + testonly = 1, + srcs = ["socket_unix_dgram.cc"], + hdrs = ["socket_unix_dgram.h"], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_unix_seqpacket_test_cases", + testonly = 1, + srcs = ["socket_unix_seqpacket.cc"], + hdrs = ["socket_unix_seqpacket.h"], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_ip_tcp_generic_test_cases", + testonly = 1, + srcs = [ + "socket_ip_tcp_generic.cc", + ], + hdrs = [ + "socket_ip_tcp_generic.h", + ], + deps = [ + ":socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_non_blocking_test_cases", + testonly = 1, + srcs = [ + "socket_non_blocking.cc", + ], + hdrs = [ + "socket_non_blocking.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_unix_non_stream_test_cases", + testonly = 1, + srcs = [ + "socket_unix_non_stream.cc", + ], + hdrs = [ + "socket_unix_non_stream.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:memory_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_non_stream_test_cases", + testonly = 1, + srcs = [ + "socket_non_stream.cc", + ], + hdrs = [ + "socket_non_stream.h", + ], + deps = [ + ":ip_socket_test_util", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_binary( + name = "socket_abstract_test", + testonly = 1, + srcs = [ + "socket_abstract.cc", + ], + linkstatic = 1, + deps = [ + ":socket_generic_test_cases", + ":socket_test_util", + ":socket_unix_test_cases", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_abstract_non_blocking_test", + testonly = 1, + srcs = [ + "socket_unix_abstract_nonblock.cc", + ], + linkstatic = 1, + deps = [ + ":socket_non_blocking_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_unix_dgram_local_test", + testonly = 1, + srcs = ["socket_unix_dgram_local.cc"], + linkstatic = 1, + deps = [ + ":socket_non_stream_test_cases", + ":socket_test_util", + ":socket_unix_dgram_test_cases", + ":socket_unix_non_stream_test_cases", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_unix_dgram_non_blocking_test", + testonly = 1, + srcs = ["socket_unix_dgram_non_blocking.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_unix_seqpacket_local_test", + testonly = 1, + srcs = [ + "socket_unix_seqpacket_local.cc", + ], + linkstatic = 1, + deps = [ + ":socket_non_stream_test_cases", + ":socket_test_util", + ":socket_unix_non_stream_test_cases", + ":socket_unix_seqpacket_test_cases", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_unix_stream_test", + testonly = 1, + srcs = ["socket_unix_stream.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_ip_tcp_generic_loopback_test", + testonly = 1, + srcs = [ + "socket_ip_tcp_generic_loopback.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_ip_tcp_generic_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_ip_tcp_udp_generic_loopback_test", + testonly = 1, + srcs = [ + "socket_ip_tcp_udp_generic.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_ip_tcp_loopback_test", + testonly = 1, + srcs = [ + "socket_ip_tcp_loopback.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_generic_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_ip_tcp_loopback_non_blocking_test", + testonly = 1, + srcs = [ + "socket_ip_tcp_loopback_nonblock.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_non_blocking_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_ip_udp_loopback_test", + testonly = 1, + srcs = [ + "socket_ip_udp_loopback.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_generic_test_cases", + ":socket_non_stream_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_ip_udp_loopback_non_blocking_test", + testonly = 1, + srcs = [ + "socket_ip_udp_loopback_nonblock.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_non_blocking_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_domain_test", + testonly = 1, + srcs = [ + "socket_unix_domain.cc", + ], + linkstatic = 1, + deps = [ + ":socket_generic_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_domain_non_blocking_test", + testonly = 1, + srcs = [ + "socket_unix_pair_nonblock.cc", + ], + linkstatic = 1, + deps = [ + ":socket_non_blocking_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_filesystem_test", + testonly = 1, + srcs = [ + "socket_filesystem.cc", + ], + linkstatic = 1, + deps = [ + ":socket_generic_test_cases", + ":socket_test_util", + ":socket_unix_test_cases", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_filesystem_non_blocking_test", + testonly = 1, + srcs = [ + "socket_unix_filesystem_nonblock.cc", + ], + linkstatic = 1, + deps = [ + ":socket_non_blocking_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_inet_loopback_test", + testonly = 1, + srcs = ["socket_inet_loopback.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_netlink_route_test", + testonly = 1, + srcs = ["socket_netlink_route.cc"], + linkstatic = 1, + deps = [ + ":socket_netlink_util", + ":socket_test_util", + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +# These socket tests are in a library because the test cases are shared +# across several test build targets. +cc_library( + name = "socket_stream_test_cases", + testonly = 1, + srcs = [ + "socket_stream.cc", + ], + hdrs = [ + "socket_stream.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_unix_test_cases", + testonly = 1, + srcs = [ + "socket_unix.cc", + ], + hdrs = [ + "socket_unix.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_stream_blocking_test_cases", + testonly = 1, + srcs = [ + "socket_stream_blocking.cc", + ], + hdrs = [ + "socket_stream_blocking.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_stream_nonblocking_test_cases", + testonly = 1, + srcs = [ + "socket_stream_nonblock.cc", + ], + hdrs = [ + "socket_stream_nonblock.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_binary( + name = "socket_stream_local_test", + testonly = 1, + srcs = [ + "socket_unix_stream_local.cc", + ], + linkstatic = 1, + deps = [ + ":socket_stream_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_stream_blocking_local_test", + testonly = 1, + srcs = [ + "socket_unix_stream_blocking_local.cc", + ], + linkstatic = 1, + deps = [ + ":socket_stream_blocking_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_stream_blocking_tcp_test", + testonly = 1, + srcs = [ + "socket_ip_tcp_loopback_blocking.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_stream_blocking_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_stream_nonblock_local_test", + testonly = 1, + srcs = [ + "socket_unix_stream_nonblock_local.cc", + ], + linkstatic = 1, + deps = [ + ":socket_stream_nonblocking_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_unix_abstract_test", + testonly = 1, + srcs = [ + "socket_unix_abstract.cc", + ], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":socket_unix_test_cases", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_unix_unbound_dgram_test", + testonly = 1, + srcs = ["socket_unix_unbound_dgram.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_unix_unbound_abstract_test", + testonly = 1, + srcs = ["socket_unix_unbound_abstract.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_unix_unbound_filesystem_test", + testonly = 1, + srcs = ["socket_unix_unbound_filesystem.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_unix_filesystem_test", + testonly = 1, + srcs = [ + "socket_unix_filesystem.cc", + ], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":socket_unix_test_cases", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "socket_non_stream_blocking_test_cases", + testonly = 1, + srcs = [ + "socket_non_stream_blocking.cc", + ], + hdrs = [ + "socket_non_stream_blocking.h", + ], + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_util", + "//test/util:thread_util", + "//test/util:timer_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_binary( + name = "socket_non_stream_blocking_local_test", + testonly = 1, + srcs = [ + "socket_unix_non_stream_blocking_local.cc", + ], + linkstatic = 1, + deps = [ + ":socket_non_stream_blocking_test_cases", + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_non_stream_blocking_udp_test", + testonly = 1, + srcs = [ + "socket_ip_udp_loopback_blocking.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_non_stream_blocking_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_unix_pair_test", + testonly = 1, + srcs = [ + "socket_unix_pair.cc", + ], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":socket_unix_test_cases", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_unix_unbound_seqpacket_test", + testonly = 1, + srcs = ["socket_unix_unbound_seqpacket.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_unix_unbound_stream_test", + testonly = 1, + srcs = ["socket_unix_unbound_stream.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + ":unix_domain_socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "socket_netdevice_test", + testonly = 1, + srcs = ["socket_netdevice.cc"], + linkstatic = 1, + deps = [ + ":socket_netlink_util", + ":socket_test_util", + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/base:endian", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "stat_test", + testonly = 1, + srcs = [ + "file_base.h", + "stat.cc", + ], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "stat_times_test", + testonly = 1, + srcs = ["stat_times.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "statfs_test", + testonly = 1, + srcs = [ + "file_base.h", + "statfs.cc", + ], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "symlink_test", + testonly = 1, + srcs = ["symlink.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sync_test", + testonly = 1, + srcs = ["sync.cc"], + linkstatic = 1, + deps = [ + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sysinfo_test", + testonly = 1, + srcs = ["sysinfo.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "syslog_test", + testonly = 1, + srcs = ["syslog.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "sysret_test", + testonly = 1, + srcs = ["sysret.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "tcp_socket_test", + testonly = 1, + srcs = ["tcp_socket.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "tgkill_test", + testonly = 1, + srcs = ["tgkill.cc"], + linkstatic = 1, + deps = [ + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "time_test", + testonly = 1, + srcs = ["time.cc"], + linkstatic = 1, + deps = [ + "//test/util:proc_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "timerfd_test", + testonly = 1, + srcs = ["timerfd.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + ], +) + +cc_binary( + name = "timers_test", + testonly = 1, + srcs = ["timers.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:logging", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:signal_util", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "tkill_test", + testonly = 1, + srcs = ["tkill.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "truncate_test", + testonly = 1, + srcs = ["truncate.cc"], + linkstatic = 1, + deps = [ + ":file_base", + "//test/util:capability_util", + "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "udp_socket_test", + testonly = 1, + srcs = ["udp_socket.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "udp_bind_test", + testonly = 1, + srcs = ["udp_bind.cc"], + linkstatic = 1, + deps = [ + ":socket_test_util", + "//test/util:file_descriptor", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "uidgid_test", + testonly = 1, + srcs = ["uidgid.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "uname_test", + testonly = 1, + srcs = ["uname.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "unlink_test", + testonly = 1, + srcs = ["unlink.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "unshare_test", + testonly = 1, + srcs = ["unshare.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/synchronization", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "utimes_test", + testonly = 1, + srcs = ["utimes.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:fs_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + ], +) + +cc_binary( + name = "vdso_test", + testonly = 1, + srcs = ["vdso.cc"], + linkstatic = 1, + deps = [ + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:proc_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "vfork_test", + testonly = 1, + srcs = ["vfork.cc"], + linkstatic = 1, + deps = [ + "//test/util:logging", + "//test/util:multiprocess_util", + "//test/util:test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "wait_test", + testonly = 1, + srcs = ["wait.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:logging", + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:signal_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "write_test", + testonly = 1, + srcs = ["write.cc"], + linkstatic = 1, + deps = [ + "//test/util:cleanup", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "memory_accounting_test", + testonly = 1, + srcs = ["memory_accounting.cc"], + linkstatic = 1, + deps = [ + "//test/util:fs_util", + "//test/util:posix_error", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "semaphore_test", + testonly = 1, + srcs = ["semaphore.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:thread_util", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "shm_test", + testonly = 1, + srcs = ["shm.cc"], + linkstatic = 1, + deps = [ + "//test/util:multiprocess_util", + "//test/util:posix_error", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/time", + ], +) + +cc_binary( + name = "fadvise64_test", + testonly = 1, + srcs = ["fadvise64.cc"], + linkstatic = 1, + deps = [ + "//test/util:file_descriptor", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "vdso_clock_gettime_test", + testonly = 1, + srcs = ["vdso_clock_gettime.cc"], + linkstatic = 1, + deps = [ + "//test/util:test_main", + "//test/util:test_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_binary( + name = "vsyscall_test", + testonly = 1, + srcs = ["vsyscall.cc"], + linkstatic = 1, + deps = [ + "//test/util:proc_util", + "//test/util:test_main", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], +) diff --git a/test/syscalls/linux/accept_bind.cc b/test/syscalls/linux/accept_bind.cc new file mode 100644 index 000000000..7c6e92317 --- /dev/null +++ b/test/syscalls/linux/accept_bind.cc @@ -0,0 +1,600 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST_P(AllSocketPairTest, Listen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, ListenIncreaseBacklog) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5), + SyscallSucceeds()); + ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 10), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, ListenDecreaseBacklog) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5), + SyscallSucceeds()); + ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 1), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, ListenWithoutBind) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(listen(sockets->first_fd(), 0), SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, DoubleBind) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, BindListenBind) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, DoubleListen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, DoubleConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallFailsWithErrno(EISCONN)); +} + +TEST_P(AllSocketPairTest, Connect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, ConnectToFilePath) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct sockaddr_un addr = {}; + addr.sun_family = AF_UNIX; + constexpr char kPath[] = "/tmp"; + memcpy(addr.sun_path, kPath, sizeof(kPath)); + + ASSERT_THAT( + connect(sockets->second_fd(), + reinterpret_cast(&addr), sizeof(addr)), + SyscallFailsWithErrno(ECONNREFUSED)); +} + +TEST_P(AllSocketPairTest, ConnectToInvalidAbstractPath) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct sockaddr_un addr = {}; + addr.sun_family = AF_UNIX; + constexpr char kPath[] = "\0nonexistent"; + memcpy(addr.sun_path, kPath, sizeof(kPath)); + + ASSERT_THAT( + connect(sockets->second_fd(), + reinterpret_cast(&addr), sizeof(addr)), + SyscallFailsWithErrno(ECONNREFUSED)); +} + +TEST_P(AllSocketPairTest, SelfConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, ConnectWithoutListen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallFailsWithErrno(ECONNREFUSED)); +} + +TEST_P(AllSocketPairTest, Accept) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + int accepted = -1; + ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr), + SyscallSucceeds()); + ASSERT_THAT(close(accepted), SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, AcceptValidAddrLen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + int accepted = -1; + struct sockaddr_un addr = {}; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT( + accepted = accept(sockets->first_fd(), + reinterpret_cast(&addr), &addr_len), + SyscallSucceeds()); + ASSERT_THAT(close(accepted), SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, AcceptNegativeAddrLen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + // With a negative addr_len, accept returns EINVAL, + struct sockaddr_un addr = {}; + socklen_t addr_len = -1; + ASSERT_THAT(accept(sockets->first_fd(), + reinterpret_cast(&addr), &addr_len), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, AcceptLargePositiveAddrLen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + // With a large (positive) addr_len, accept does not return EINVAL. + int accepted = -1; + char addr_buf[200]; + socklen_t addr_len = sizeof(addr_buf); + ASSERT_THAT(accepted = accept(sockets->first_fd(), + reinterpret_cast(addr_buf), + &addr_len), + SyscallSucceeds()); + // addr_len should have been updated by accept(). + EXPECT_LT(addr_len, sizeof(addr_buf)); + ASSERT_THAT(close(accepted), SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, AcceptVeryLargePositiveAddrLen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + // With a large (positive) addr_len, accept does not return EINVAL. + int accepted = -1; + char addr_buf[2000]; + socklen_t addr_len = sizeof(addr_buf); + ASSERT_THAT(accepted = accept(sockets->first_fd(), + reinterpret_cast(addr_buf), + &addr_len), + SyscallSucceeds()); + // addr_len should have been updated by accept(). + EXPECT_LT(addr_len, sizeof(addr_buf)); + ASSERT_THAT(close(accepted), SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, AcceptWithoutBind) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(accept(sockets->first_fd(), nullptr, nullptr), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, AcceptWithoutListen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(accept(sockets->first_fd(), nullptr, nullptr), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, GetRemoteAddress) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + socklen_t addr_len = sockets->first_addr_size(); + struct sockaddr_storage addr = {}; + ASSERT_THAT( + getpeername(sockets->second_fd(), (struct sockaddr*)(&addr), &addr_len), + SyscallSucceeds()); + EXPECT_EQ(addr_len, sockets->first_addr_len()); + EXPECT_EQ(0, memcmp(&addr, sockets->first_addr(), sockets->first_addr_len())); +} + +TEST_P(AllSocketPairTest, UnboundGetLocalAddress) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + socklen_t addr_len = sockets->first_addr_size(); + struct sockaddr_storage addr = {}; + ASSERT_THAT( + getsockname(sockets->second_fd(), (struct sockaddr*)(&addr), &addr_len), + SyscallSucceeds()); + EXPECT_EQ(addr_len, 2); + EXPECT_EQ( + memcmp(&addr, sockets->second_addr(), + std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), + 0); +} + +TEST_P(AllSocketPairTest, BoundGetLocalAddress) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + socklen_t addr_len = sockets->first_addr_size(); + struct sockaddr_storage addr = {}; + ASSERT_THAT( + getsockname(sockets->second_fd(), (struct sockaddr*)(&addr), &addr_len), + SyscallSucceeds()); + EXPECT_EQ(addr_len, sockets->second_addr_len()); + EXPECT_EQ( + memcmp(&addr, sockets->second_addr(), + std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), + 0); +} + +TEST_P(AllSocketPairTest, BoundConnector) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, UnboundSenderAddr) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + int accepted = -1; + ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr), + SyscallSucceeds()); + FileDescriptor accepted_fd(accepted); + + int i = 0; + ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), + SyscallSucceedsWithValue(sizeof(i))); + + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT( + RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, + reinterpret_cast(&addr), &addr_len), + SyscallSucceedsWithValue(sizeof(i))); + if (!IsRunningOnGvisor()) { + // Linux returns a zero length for addresses from recvfrom(2) and + // recvmsg(2). This differs from the behavior of getpeername(2) and + // getsockname(2). For simplicity, we use the getpeername(2) and + // getsockname(2) behavior for recvfrom(2) and recvmsg(2). + EXPECT_EQ(addr_len, 0); + return; + } + EXPECT_EQ(addr_len, 2); + EXPECT_EQ( + memcmp(&addr, sockets->second_addr(), + std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), + 0); +} + +TEST_P(AllSocketPairTest, BoundSenderAddr) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + int accepted = -1; + ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr), + SyscallSucceeds()); + FileDescriptor accepted_fd(accepted); + + int i = 0; + ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), + SyscallSucceedsWithValue(sizeof(i))); + + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT( + RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, + reinterpret_cast(&addr), &addr_len), + SyscallSucceedsWithValue(sizeof(i))); + EXPECT_EQ(addr_len, sockets->second_addr_len()); + EXPECT_EQ( + memcmp(&addr, sockets->second_addr(), + std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), + 0); +} + +TEST_P(AllSocketPairTest, BindAfterConnectSenderAddr) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallSucceeds()); + + int accepted = -1; + ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr), + SyscallSucceeds()); + FileDescriptor accepted_fd(accepted); + + int i = 0; + ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), + SyscallSucceedsWithValue(sizeof(i))); + + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT( + RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, + reinterpret_cast(&addr), &addr_len), + SyscallSucceedsWithValue(sizeof(i))); + EXPECT_EQ(addr_len, sockets->second_addr_len()); + EXPECT_EQ( + memcmp(&addr, sockets->second_addr(), + std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), + 0); +} + +TEST_P(AllSocketPairTest, BindAfterAcceptSenderAddr) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + int accepted = -1; + ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr), + SyscallSucceeds()); + FileDescriptor accepted_fd(accepted); + + ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallSucceeds()); + + int i = 0; + ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), + SyscallSucceedsWithValue(sizeof(i))); + + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT( + RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, + reinterpret_cast(&addr), &addr_len), + SyscallSucceedsWithValue(sizeof(i))); + EXPECT_EQ(addr_len, sockets->second_addr_len()); + EXPECT_EQ( + memcmp(&addr, sockets->second_addr(), + std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), + 0); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(VecCat( + ApplyVec( + FilesystemUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/accept_bind_stream.cc b/test/syscalls/linux/accept_bind_stream.cc new file mode 100644 index 000000000..f7113a6fc --- /dev/null +++ b/test/syscalls/linux/accept_bind_stream.cc @@ -0,0 +1,93 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST_P(AllSocketPairTest, BoundSenderAddrCoalesced) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); + + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + int accepted = -1; + ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr), + SyscallSucceeds()); + FileDescriptor closer(accepted); + + int i = 0; + ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), + SyscallSucceedsWithValue(sizeof(i))); + + ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), + sockets->second_addr_size()), + SyscallSucceeds()); + + i = 0; + ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), + SyscallSucceedsWithValue(sizeof(i))); + + int ri[2] = {0, 0}; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT( + RetryEINTR(recvfrom)(accepted, ri, sizeof(ri), 0, + reinterpret_cast(&addr), &addr_len), + SyscallSucceedsWithValue(sizeof(ri))); + EXPECT_EQ(addr_len, sockets->second_addr_len()); + + EXPECT_EQ( + memcmp(&addr, sockets->second_addr(), + std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), + 0); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(VecCat( + ApplyVec( + FilesystemUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/access.cc b/test/syscalls/linux/access.cc new file mode 100644 index 000000000..6ea070a5d --- /dev/null +++ b/test/syscalls/linux/access.cc @@ -0,0 +1,170 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +using ::testing::Ge; + +namespace gvisor { +namespace testing { + +namespace { + +class AccessTest : public ::testing::Test { + public: + std::string CreateTempFile(int perm) { + const std::string path = NewTempAbsPath(); + const int fd = open(path.c_str(), O_CREAT | O_RDONLY, perm); + TEST_PCHECK(fd > 0); + TEST_PCHECK(close(fd) == 0); + return path; + } + + protected: + // SetUp creates various configurations of files. + void SetUp() override { + // Move to the temporary directory. This allows us to reason more easily + // about absolute and relative paths. + ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); + + // Create an empty file, standard permissions. + relfile_ = NewTempRelPath(); + int fd; + ASSERT_THAT(fd = open(relfile_.c_str(), O_CREAT | O_TRUNC, 0644), + SyscallSucceedsWithValue(Ge(0))); + ASSERT_THAT(close(fd), SyscallSucceeds()); + absfile_ = GetAbsoluteTestTmpdir() + "/" + relfile_; + + // Create an empty directory, no writable permissions. + absdir_ = NewTempAbsPath(); + reldir_ = JoinPath(Basename(absdir_), ""); + ASSERT_THAT(mkdir(reldir_.c_str(), 0555), SyscallSucceeds()); + + // This file doesn't exist. + relnone_ = NewTempRelPath(); + absnone_ = GetAbsoluteTestTmpdir() + "/" + relnone_; + } + + // TearDown unlinks created files. + void TearDown() override { + ASSERT_THAT(unlink(absfile_.c_str()), SyscallSucceeds()); + ASSERT_THAT(rmdir(absdir_.c_str()), SyscallSucceeds()); + } + + std::string relfile_; + std::string reldir_; + + std::string absfile_; + std::string absdir_; + + std::string relnone_; + std::string absnone_; +}; + +TEST_F(AccessTest, RelativeFile) { + EXPECT_THAT(access(relfile_.c_str(), R_OK), SyscallSucceeds()); +} + +TEST_F(AccessTest, RelativeDir) { + EXPECT_THAT(access(reldir_.c_str(), R_OK | X_OK), SyscallSucceeds()); +} + +TEST_F(AccessTest, AbsFile) { + EXPECT_THAT(access(absfile_.c_str(), R_OK), SyscallSucceeds()); +} + +TEST_F(AccessTest, AbsDir) { + EXPECT_THAT(access(absdir_.c_str(), R_OK | X_OK), SyscallSucceeds()); +} + +TEST_F(AccessTest, RelDoesNotExist) { + EXPECT_THAT(access(relnone_.c_str(), R_OK), SyscallFailsWithErrno(ENOENT)); +} + +TEST_F(AccessTest, AbsDoesNotExist) { + EXPECT_THAT(access(absnone_.c_str(), R_OK), SyscallFailsWithErrno(ENOENT)); +} + +TEST_F(AccessTest, InvalidMode) { + EXPECT_THAT(access(relfile_.c_str(), 0xffffffff), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(AccessTest, NoPerms) { + // Drop capabilities that allow us to override permissions. We must drop + // PERMITTED because access() checks those instead of EFFECTIVE. + ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_OVERRIDE)); + ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_READ_SEARCH)); + + EXPECT_THAT(access(absdir_.c_str(), W_OK), SyscallFailsWithErrno(EACCES)); +} + +TEST_F(AccessTest, InvalidName) { + EXPECT_THAT(access(reinterpret_cast(0x1234), W_OK), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(AccessTest, UsrReadOnly) { + // Drop capabilities that allow us to override permissions. We must drop + // PERMITTED because access() checks those instead of EFFECTIVE. + ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_OVERRIDE)); + ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_READ_SEARCH)); + + const std::string filename = CreateTempFile(0400); + EXPECT_THAT(access(filename.c_str(), R_OK), SyscallSucceeds()); + EXPECT_THAT(access(filename.c_str(), W_OK), SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(access(filename.c_str(), X_OK), SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds()); +} + +TEST_F(AccessTest, UsrReadExec) { + // Drop capabilities that allow us to override permissions. We must drop + // PERMITTED because access() checks those instead of EFFECTIVE. + ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_OVERRIDE)); + ASSERT_NO_ERRNO(DropPermittedCapability(CAP_DAC_READ_SEARCH)); + + const std::string filename = CreateTempFile(0500); + EXPECT_THAT(access(filename.c_str(), R_OK | X_OK), SyscallSucceeds()); + EXPECT_THAT(access(filename.c_str(), W_OK), SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds()); +} + +TEST_F(AccessTest, UsrReadWrite) { + const std::string filename = CreateTempFile(0600); + EXPECT_THAT(access(filename.c_str(), R_OK | W_OK), SyscallSucceeds()); + EXPECT_THAT(access(filename.c_str(), X_OK), SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds()); +} + +TEST_F(AccessTest, UsrReadWriteExec) { + const std::string filename = CreateTempFile(0700); + EXPECT_THAT(access(filename.c_str(), R_OK | W_OK | X_OK), SyscallSucceeds()); + EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/affinity.cc b/test/syscalls/linux/affinity.cc new file mode 100644 index 000000000..8a16343d5 --- /dev/null +++ b/test/syscalls/linux/affinity.cc @@ -0,0 +1,241 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/str_split.h" +#include "test/util/cleanup.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { +namespace { + +// These tests are for both the sched_getaffinity(2) and sched_setaffinity(2) +// syscalls. +class AffinityTest : public ::testing::Test { + protected: + void SetUp() override { + EXPECT_THAT( + // Needs use the raw syscall to get the actual size. + cpuset_size_ = syscall(SYS_sched_getaffinity, /*pid=*/0, + sizeof(cpu_set_t), &mask_), + SyscallSucceeds()); + // Lots of tests rely on having more than 1 logical processor available. + EXPECT_GT(CPU_COUNT(&mask_), 1); + } + + static PosixError ClearLowestBit(cpu_set_t* mask, size_t cpus) { + const size_t mask_size = CPU_ALLOC_SIZE(cpus); + for (size_t n = 0; n < cpus; ++n) { + if (CPU_ISSET_S(n, mask_size, mask)) { + CPU_CLR_S(n, mask_size, mask); + return NoError(); + } + } + return PosixError(EINVAL, "No bit to clear, mask is empty"); + } + + PosixError ClearLowestBit() { return ClearLowestBit(&mask_, CPU_SETSIZE); } + + // Stores the initial cpu mask for this process. + cpu_set_t mask_ = {}; + int cpuset_size_ = 0; +}; + +// sched_getaffinity(2) is implemented. +TEST_F(AffinityTest, SchedGetAffinityImplemented) { + EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_), + SyscallSucceeds()); +} + +// PID is not found. +TEST_F(AffinityTest, SchedGetAffinityInvalidPID) { + // Flaky, but it's tough to avoid a race condition when finding an unused pid + EXPECT_THAT(sched_getaffinity(/*pid=*/INT_MAX - 1, sizeof(cpu_set_t), &mask_), + SyscallFailsWithErrno(ESRCH)); +} + +// PID is not found. +TEST_F(AffinityTest, SchedSetAffinityInvalidPID) { + // Flaky, but it's tough to avoid a race condition when finding an unused pid + EXPECT_THAT(sched_setaffinity(/*pid=*/INT_MAX - 1, sizeof(cpu_set_t), &mask_), + SyscallFailsWithErrno(ESRCH)); +} + +TEST_F(AffinityTest, SchedSetAffinityZeroMask) { + CPU_ZERO(&mask_); + EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_), + SyscallFailsWithErrno(EINVAL)); +} + +// N.B. This test case relies on cpuset_size_ larger than the actual number of +// of all existing CPUs. Check your machine if the test fails. +TEST_F(AffinityTest, SchedSetAffinityNonexistentCPUDropped) { + cpu_set_t mask = mask_; + // Add a nonexistent CPU. + // + // The number needs to be larger than the possible number of CPU available, + // but smaller than the number of the CPU that the kernel claims to support -- + // it's implicitly returned by raw sched_getaffinity syscall. + CPU_SET(cpuset_size_ * 8 - 1, &mask); + EXPECT_THAT( + // Use raw syscall because it will be rejected by the libc wrapper + // otherwise. + syscall(SYS_sched_setaffinity, /*pid=*/0, sizeof(cpu_set_t), &mask), + SyscallSucceeds()) + << "failed with cpumask : " << CPUSetToString(mask) + << ", cpuset_size_ : " << cpuset_size_; + cpu_set_t newmask; + EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &newmask), + SyscallSucceeds()); + EXPECT_TRUE(CPU_EQUAL(&mask_, &newmask)) + << "got: " << CPUSetToString(newmask) + << " != expected: " << CPUSetToString(mask_); +} + +TEST_F(AffinityTest, SchedSetAffinityOnlyNonexistentCPUFails) { + // Make an empty cpu set. + CPU_ZERO(&mask_); + // Add a nonexistent CPU. + // + // The number needs to be larger than the possible number of CPU available, + // but smaller than the number of the CPU that the kernel claims to support -- + // it's implicitly returned by raw sched_getaffinity syscall. + int cpu = cpuset_size_ * 8 - 1; + if (cpu <= NumCPUs()) { + LOG(INFO) << "Skipping test: cpu " << cpu << " exists"; + return; + } + CPU_SET(cpu, &mask_); + EXPECT_THAT( + // Use raw syscall because it will be rejected by the libc wrapper + // otherwise. + syscall(SYS_sched_setaffinity, /*pid=*/0, sizeof(cpu_set_t), &mask_), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(AffinityTest, SchedSetAffinityInvalidSize) { + EXPECT_GT(cpuset_size_, 0); + // Not big enough. + EXPECT_THAT(sched_getaffinity(/*pid=*/0, cpuset_size_ - 1, &mask_), + SyscallFailsWithErrno(EINVAL)); + // Not a multiple of word size. + EXPECT_THAT(sched_getaffinity(/*pid=*/0, cpuset_size_ + 1, &mask_), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(AffinityTest, Sanity) { + ASSERT_NO_ERRNO(ClearLowestBit()); + EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_), + SyscallSucceeds()); + cpu_set_t newmask; + EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &newmask), + SyscallSucceeds()); + EXPECT_TRUE(CPU_EQUAL(&mask_, &newmask)) + << "got: " << CPUSetToString(newmask) + << " != expected: " << CPUSetToString(mask_); +} + +TEST_F(AffinityTest, NewThread) { + ASSERT_NO_ERRNO(ClearLowestBit()); + ASSERT_NO_ERRNO(ClearLowestBit()); + EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &mask_), + SyscallSucceeds()); + ScopedThread([this]() { + cpu_set_t child_mask; + ASSERT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &child_mask), + SyscallSucceeds()); + ASSERT_TRUE(CPU_EQUAL(&child_mask, &mask_)) + << "child cpu mask: " << CPUSetToString(child_mask) + << " != parent cpu mask: " << CPUSetToString(mask_); + }); +} + +TEST_F(AffinityTest, ConsistentWithProcCpuInfo) { + // Count how many cpus are shown in /proc/cpuinfo. + std::string cpuinfo = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/cpuinfo")); + int count = 0; + for (auto const& line : absl::StrSplit(cpuinfo, '\n')) { + if (absl::StartsWith(line, "processor")) { + count++; + } + } + EXPECT_GE(count, CPU_COUNT(&mask_)); +} + +TEST_F(AffinityTest, ConsistentWithProcStat) { + // Count how many cpus are shown in /proc/stat. + std::string stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat")); + int count = 0; + for (auto const& line : absl::StrSplit(stat, '\n')) { + if (absl::StartsWith(line, "cpu") && !absl::StartsWith(line, "cpu ")) { + count++; + } + } + EXPECT_GE(count, CPU_COUNT(&mask_)); +} + +TEST_F(AffinityTest, SmallCpuMask) { + const int num_cpus = NumCPUs(); + const size_t mask_size = CPU_ALLOC_SIZE(num_cpus); + cpu_set_t* mask = CPU_ALLOC(num_cpus); + ASSERT_NE(mask, nullptr); + const auto free_mask = Cleanup([&] { CPU_FREE(mask); }); + + CPU_ZERO_S(mask_size, mask); + ASSERT_THAT(sched_getaffinity(0, mask_size, mask), SyscallSucceeds()); +} + +TEST_F(AffinityTest, LargeCpuMask) { + // Allocate mask bigger than cpu_set_t normally allocates. + const size_t cpus = CPU_SETSIZE * 8; + const size_t mask_size = CPU_ALLOC_SIZE(cpus); + + cpu_set_t* large_mask = CPU_ALLOC(cpus); + auto free_mask = Cleanup([large_mask] { CPU_FREE(large_mask); }); + CPU_ZERO_S(mask_size, large_mask); + + // Check that get affinity with large mask works as expected. + ASSERT_THAT(sched_getaffinity(/*pid=*/0, mask_size, large_mask), + SyscallSucceeds()); + EXPECT_TRUE(CPU_EQUAL(&mask_, large_mask)) + << "got: " << CPUSetToString(*large_mask, cpus) + << " != expected: " << CPUSetToString(mask_); + + // Check that set affinity with large mask works as expected. + ASSERT_NO_ERRNO(ClearLowestBit(large_mask, cpus)); + EXPECT_THAT(sched_setaffinity(/*pid=*/0, mask_size, large_mask), + SyscallSucceeds()); + + cpu_set_t* new_mask = CPU_ALLOC(cpus); + auto free_new_mask = Cleanup([new_mask] { CPU_FREE(new_mask); }); + CPU_ZERO_S(mask_size, new_mask); + EXPECT_THAT(sched_getaffinity(/*pid=*/0, mask_size, new_mask), + SyscallSucceeds()); + + EXPECT_TRUE(CPU_EQUAL_S(mask_size, large_mask, new_mask)) + << "got: " << CPUSetToString(*new_mask, cpus) + << " != expected: " << CPUSetToString(*large_mask, cpus); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/aio.cc b/test/syscalls/linux/aio.cc new file mode 100644 index 000000000..cc5392223 --- /dev/null +++ b/test/syscalls/linux/aio.cc @@ -0,0 +1,433 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/syscalls/linux/file_base.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +constexpr char kData[] = "hello world!"; + +int SubmitCtx(aio_context_t ctx, long nr, struct iocb** iocbpp) { + return syscall(__NR_io_submit, ctx, nr, iocbpp); +} + +} // namespace + +class AIOTest : public FileTest { + public: + AIOTest() : ctx_(0) {} + + int SetupContext(unsigned int nr) { + return syscall(__NR_io_setup, nr, &ctx_); + } + + int Submit(long nr, struct iocb** iocbpp) { + return SubmitCtx(ctx_, nr, iocbpp); + } + + int GetEvents(long min, long max, struct io_event* events, + struct timespec* timeout) { + return RetryEINTR(syscall)(__NR_io_getevents, ctx_, min, max, events, + timeout); + } + + int DestroyContext() { return syscall(__NR_io_destroy, ctx_); } + + void TearDown() override { + FileTest::TearDown(); + if (ctx_ != 0) { + ASSERT_THAT(DestroyContext(), SyscallSucceeds()); + } + } + + struct iocb CreateCallback() { + struct iocb cb = {}; + cb.aio_data = 0x123; + cb.aio_fildes = test_file_fd_.get(); + cb.aio_lio_opcode = IOCB_CMD_PWRITE; + cb.aio_buf = reinterpret_cast(kData); + cb.aio_offset = 0; + cb.aio_nbytes = strlen(kData); + return cb; + } + + protected: + aio_context_t ctx_; +}; + +TEST_F(AIOTest, BasicWrite) { + // Copied from fs/aio.c. + constexpr unsigned AIO_RING_MAGIC = 0xa10a10a1; + struct aio_ring { + unsigned id; + unsigned nr; + unsigned head; + unsigned tail; + unsigned magic; + unsigned compat_features; + unsigned incompat_features; + unsigned header_length; + struct io_event io_events[0]; + }; + + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + // Check that 'ctx_' points to a valid address. libaio uses it to check if + // aio implementation uses aio_ring. gVisor doesn't and returns all zeroes. + // Linux implements aio_ring, so skip the zeroes check. + // + // TODO: Remove when gVisor implements aio_ring. + auto ring = reinterpret_cast(ctx_); + auto magic = IsRunningOnGvisor() ? 0 : AIO_RING_MAGIC; + EXPECT_EQ(ring->magic, magic); + + struct iocb cb = CreateCallback(); + struct iocb* cbs[1] = {&cb}; + + // Submit the request. + ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1)); + + // Get the reply. + struct io_event events[1]; + ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1)); + + // Verify that it is as expected. + EXPECT_EQ(events[0].data, 0x123); + EXPECT_EQ(events[0].obj, reinterpret_cast(&cb)); + EXPECT_EQ(events[0].res, strlen(kData)); + + // Verify that the file contains the contents. + char verify_buf[32] = {}; + ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)), + SyscallSucceeds()); + EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0); +} + +TEST_F(AIOTest, BadWrite) { + // Create a pipe and immediately close the read end. + int pipefd[2]; + ASSERT_THAT(pipe(pipefd), SyscallSucceeds()); + + FileDescriptor rfd(pipefd[0]); + FileDescriptor wfd(pipefd[1]); + + rfd.reset(); // Close the read end. + + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + struct iocb cb = CreateCallback(); + // Try to write to the read end. + cb.aio_fildes = wfd.get(); + struct iocb* cbs[1] = {&cb}; + + // Submit the request. + ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1)); + + // Get the reply. + struct io_event events[1]; + ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1)); + + // Verify that it fails with the right error code. + EXPECT_EQ(events[0].data, 0x123); + EXPECT_EQ(events[0].obj, reinterpret_cast(&cb)); + EXPECT_LT(events[0].res, 0); +} + +TEST_F(AIOTest, ExitWithPendingIo) { + // Setup a context that is 5 entries deep. + ASSERT_THAT(SetupContext(5), SyscallSucceeds()); + + struct iocb cb = CreateCallback(); + struct iocb* cbs[] = {&cb}; + + // Submit a request but don't complete it to make it pending. + EXPECT_THAT(Submit(1, cbs), SyscallSucceeds()); +} + +int Submitter(void* arg) { + auto test = reinterpret_cast(arg); + + struct iocb cb = test->CreateCallback(); + struct iocb* cbs[1] = {&cb}; + + // Submit the request. + TEST_CHECK(test->Submit(1, cbs) == 1); + return 0; +} + +TEST_F(AIOTest, CloneVm) { + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + const size_t kStackSize = 5 * kPageSize; + std::unique_ptr stack(new char[kStackSize]); + char* bp = stack.get() + kStackSize; + pid_t child; + ASSERT_THAT(child = clone(Submitter, bp, CLONE_VM | SIGCHLD, + reinterpret_cast(this)), + SyscallSucceeds()); + + // Get the reply. + struct io_event events[1]; + ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1)); + + // Verify that it is as expected. + EXPECT_EQ(events[0].data, 0x123); + EXPECT_EQ(events[0].res, strlen(kData)); + + // Verify that the file contains the contents. + char verify_buf[32] = {}; + ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)), + SyscallSucceeds()); + EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +// Tests that AIO context can be remapped to a different address. +TEST_F(AIOTest, Mremap) { + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + struct iocb cb = CreateCallback(); + struct iocb* cbs[1] = {&cb}; + + // Reserve address space for the mremap target so we have something safe to + // map over. + // + // N.B. We reserve 2 pages because we'll attempt to remap to 2 pages below. + // That should fail with EFAULT, but will fail with EINVAL if this mmap + // returns the page immediately below ctx_, as + // [new_address, new_address+2*kPageSize) overlaps [ctx_, ctx_+kPageSize). + void* new_address = mmap(nullptr, 2 * kPageSize, PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_THAT(reinterpret_cast(new_address), SyscallSucceeds()); + auto mmap_cleanup = Cleanup([new_address] { + EXPECT_THAT(munmap(new_address, 2 * kPageSize), SyscallSucceeds()); + }); + + // Test that remapping to a larger address fails. + void* res = mremap(reinterpret_cast(ctx_), kPageSize, 2 * kPageSize, + MREMAP_FIXED | MREMAP_MAYMOVE, new_address); + ASSERT_THAT(reinterpret_cast(res), SyscallFailsWithErrno(EFAULT)); + + // Remap context 'handle' to a different address. + res = mremap(reinterpret_cast(ctx_), kPageSize, kPageSize, + MREMAP_FIXED | MREMAP_MAYMOVE, new_address); + ASSERT_THAT( + reinterpret_cast(res), + SyscallSucceedsWithValue(reinterpret_cast(new_address))); + mmap_cleanup.Release(); + aio_context_t old_ctx = ctx_; + ctx_ = reinterpret_cast(new_address); + + // Check that submitting the request with the old 'ctx_' fails. + ASSERT_THAT(SubmitCtx(old_ctx, 1, cbs), SyscallFailsWithErrno(EINVAL)); + + // Submit the request with the new 'ctx_'. + ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1)); + + // Remap again. + new_address = + mmap(nullptr, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_THAT(reinterpret_cast(new_address), SyscallSucceeds()); + auto mmap_cleanup2 = Cleanup([new_address] { + EXPECT_THAT(munmap(new_address, kPageSize), SyscallSucceeds()); + }); + res = mremap(reinterpret_cast(ctx_), kPageSize, kPageSize, + MREMAP_FIXED | MREMAP_MAYMOVE, new_address); + ASSERT_THAT(reinterpret_cast(res), + SyscallSucceedsWithValue(reinterpret_cast(new_address))); + mmap_cleanup2.Release(); + ctx_ = reinterpret_cast(new_address); + + // Get the reply with yet another 'ctx_' and verify it. + struct io_event events[1]; + ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1)); + EXPECT_EQ(events[0].data, 0x123); + EXPECT_EQ(events[0].obj, reinterpret_cast(&cb)); + EXPECT_EQ(events[0].res, strlen(kData)); + + // Verify that the file contains the contents. + char verify_buf[32] = {}; + ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)), + SyscallSucceeds()); + EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0); +} + +// Tests that AIO context can be replaced with a different mapping at the same +// address and continue working. Don't ask why, but Linux allows it. +TEST_F(AIOTest, MremapOver) { + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + struct iocb cb = CreateCallback(); + struct iocb* cbs[1] = {&cb}; + + ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1)); + + // Allocate a new VMA, copy 'ctx_' content over, and remap it on top + // of 'ctx_'. + void* new_address = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_THAT(reinterpret_cast(new_address), SyscallSucceeds()); + auto mmap_cleanup = Cleanup([new_address] { + EXPECT_THAT(munmap(new_address, kPageSize), SyscallSucceeds()); + }); + + memcpy(new_address, reinterpret_cast(ctx_), kPageSize); + void* res = + mremap(new_address, kPageSize, kPageSize, MREMAP_FIXED | MREMAP_MAYMOVE, + reinterpret_cast(ctx_)); + ASSERT_THAT(reinterpret_cast(res), SyscallSucceedsWithValue(ctx_)); + mmap_cleanup.Release(); + + // Everything continues to work just fine. + struct io_event events[1]; + ASSERT_THAT(GetEvents(1, 1, events, nullptr), SyscallSucceedsWithValue(1)); + EXPECT_EQ(events[0].data, 0x123); + EXPECT_EQ(events[0].obj, reinterpret_cast(&cb)); + EXPECT_EQ(events[0].res, strlen(kData)); + + // Verify that the file contains the contents. + char verify_buf[32] = {}; + ASSERT_THAT(read(test_file_fd_.get(), &verify_buf[0], strlen(kData)), + SyscallSucceeds()); + EXPECT_EQ(strcmp(kData, &verify_buf[0]), 0); +} + +// Tests that AIO calls fail if context's address is inaccessible. +TEST_F(AIOTest, Mprotect) { + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + struct iocb cb = CreateCallback(); + struct iocb* cbs[1] = {&cb}; + + ASSERT_THAT(Submit(1, cbs), SyscallSucceedsWithValue(1)); + + // Makes the context 'handle' inaccessible and check that all subsequent + // calls fail. + ASSERT_THAT(mprotect(reinterpret_cast(ctx_), kPageSize, PROT_NONE), + SyscallSucceeds()); + struct io_event events[1]; + EXPECT_THAT(GetEvents(1, 1, events, nullptr), SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT(Submit(1, cbs), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(DestroyContext(), SyscallFailsWithErrno(EINVAL)); + + // Prevent TearDown from attempting to destroy the context and fail. + ctx_ = 0; +} + +TEST_F(AIOTest, Timeout) { + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + struct timespec timeout; + timeout.tv_sec = 0; + timeout.tv_nsec = 10; + struct io_event events[1]; + ASSERT_THAT(GetEvents(1, 1, events, &timeout), SyscallSucceedsWithValue(0)); +} + +class AIOReadWriteParamTest : public AIOTest, + public ::testing::WithParamInterface {}; + +TEST_P(AIOReadWriteParamTest, BadOffset) { + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + struct iocb cb = CreateCallback(); + struct iocb* cbs[1] = {&cb}; + + // Create a buffer that we can write to. + char buf[] = "hello world!"; + cb.aio_buf = reinterpret_cast(buf); + + // Set the operation on the callback and give a negative offset. + const int opcode = GetParam(); + cb.aio_lio_opcode = opcode; + + iovec iov = {}; + if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) { + // Create a valid iovec and set it in the callback. + iov.iov_base = reinterpret_cast(buf); + iov.iov_len = 1; + cb.aio_buf = reinterpret_cast(&iov); + // aio_nbytes is the number of iovecs. + cb.aio_nbytes = 1; + } + + // Pass a negative offset. + cb.aio_offset = -1; + + // Should get error on submission. + ASSERT_THAT(Submit(1, cbs), SyscallFailsWithErrno(EINVAL)); +} + +INSTANTIATE_TEST_CASE_P(BadOffset, AIOReadWriteParamTest, + ::testing::Values(IOCB_CMD_PREAD, IOCB_CMD_PWRITE, + IOCB_CMD_PREADV, IOCB_CMD_PWRITEV)); + +class AIOVectorizedParamTest : public AIOTest, + public ::testing::WithParamInterface {}; + +TEST_P(AIOVectorizedParamTest, BadIOVecs) { + // Setup a context that is 128 entries deep. + ASSERT_THAT(SetupContext(128), SyscallSucceeds()); + + struct iocb cb = CreateCallback(); + struct iocb* cbs[1] = {&cb}; + + // Modify the callback to use the operation from the param. + cb.aio_lio_opcode = GetParam(); + + // Create an iovec with address in kernel range, and pass that as the buffer. + iovec iov = {}; + iov.iov_base = reinterpret_cast(0xFFFFFFFF00000000); + iov.iov_len = 1; + cb.aio_buf = reinterpret_cast(&iov); + // aio_nbytes is the number of iovecs. + cb.aio_nbytes = 1; + + // Should get error on submission. + ASSERT_THAT(Submit(1, cbs), SyscallFailsWithErrno(EFAULT)); +} + +INSTANTIATE_TEST_CASE_P(BadIOVecs, AIOVectorizedParamTest, + ::testing::Values(IOCB_CMD_PREADV, IOCB_CMD_PWRITEV)); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/alarm.cc b/test/syscalls/linux/alarm.cc new file mode 100644 index 000000000..e0ddbb415 --- /dev/null +++ b/test/syscalls/linux/alarm.cc @@ -0,0 +1,193 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// N.B. Below, main blocks SIGALRM. Test cases must unblock it if they want +// delivery. + +void do_nothing_handler(int sig, siginfo_t* siginfo, void* arg) {} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and read. +TEST(AlarmTest, Interrupt_NoRandomSave) { + int pipe_fds[2]; + ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); + + FileDescriptor read_fd(pipe_fds[0]); + FileDescriptor write_fd(pipe_fds[1]); + + // Use a signal handler that interrupts but does nothing rather than using the + // default terminate action. + struct sigaction sa; + sa.sa_sigaction = do_nothing_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after read blocks below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + + char buf; + ASSERT_THAT(read(read_fd.get(), &buf, 1), SyscallFailsWithErrno(EINTR)); +} + +/* Count of the number of SIGALARMS handled. */ +static volatile int alarms_received = 0; + +void inc_alarms_handler(int sig, siginfo_t* siginfo, void* arg) { + alarms_received++; +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and read. +TEST(AlarmTest, Restart_NoRandomSave) { + alarms_received = 0; + + int pipe_fds[2]; + ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); + + FileDescriptor read_fd(pipe_fds[0]); + // Write end closed by thread below. + + struct sigaction sa; + sa.sa_sigaction = inc_alarms_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Spawn a thread to eventually unblock the read below. + ScopedThread t([pipe_fds] { + absl::SleepFor(absl::Seconds(30)); + EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); + }); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after read blocks below, but + // before it returns. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + + // Read and eventually get an EOF from the writer closing. If SA_RESTART + // didn't work, then the alarm would not have fired and we wouldn't increment + // our alarms_received count in our signal handler, or we would have not + // restarted the syscall gracefully, which we expect below in order to be + // able to get the final EOF on the pipe. + char buf; + ASSERT_THAT(read(read_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_EQ(alarms_received, 1); + + t.Join(); +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and pause. +TEST(AlarmTest, SaSiginfo_NoRandomSave) { + // Use a signal handler that interrupts but does nothing rather than using the + // default terminate action. + struct sigaction sa; + sa.sa_sigaction = do_nothing_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after pause blocks below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + ASSERT_THAT(pause(), SyscallFailsWithErrno(EINTR)); +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and pause. +TEST(AlarmTest, SaInterrupt_NoRandomSave) { + // Use a signal handler that interrupts but does nothing rather than using the + // default terminate action. + struct sigaction sa; + sa.sa_sigaction = do_nothing_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_INTERRUPT; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after pause blocks below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + ASSERT_THAT(pause(), SyscallFailsWithErrno(EINTR)); +} + +TEST(AlarmTest, UserModeSpinning) { + alarms_received = 0; + + struct sigaction sa = {}; + sa.sa_sigaction = inc_alarms_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well into the loop below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + // Make sure that the signal gets delivered even if we are spinning in user + // mode when it arrives. + while (!alarms_received) { + } +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + // These tests depend on delivering SIGALRM to the main thread. Block SIGALRM + // so that any other threads created by TestInit will also have SIGALRM + // blocked. + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); + + gvisor::testing::TestInit(&argc, &argv); + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/arch_prctl.cc b/test/syscalls/linux/arch_prctl.cc new file mode 100644 index 000000000..5687ceb86 --- /dev/null +++ b/test/syscalls/linux/arch_prctl.cc @@ -0,0 +1,48 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +// glibc does not provide a prototype for arch_prctl() so declare it here. +extern "C" int arch_prctl(int code, uintptr_t addr); + +namespace gvisor { +namespace testing { + +namespace { + +TEST(ArchPrctlTest, GetSetFS) { + uintptr_t orig; + const uintptr_t kNonCanonicalFsbase = 0x4141414142424242; + + // Get the original FS.base and then set it to the same value (this is + // intentional because FS.base is the TLS pointer so we cannot change it + // arbitrarily). + ASSERT_THAT(arch_prctl(ARCH_GET_FS, reinterpret_cast(&orig)), + SyscallSucceeds()); + ASSERT_THAT(arch_prctl(ARCH_SET_FS, orig), SyscallSucceeds()); + + // Trying to set FS.base to a non-canonical value should return an error. + ASSERT_THAT(arch_prctl(ARCH_SET_FS, kNonCanonicalFsbase), + SyscallFailsWithErrno(EPERM)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/bad.cc b/test/syscalls/linux/bad.cc new file mode 100644 index 000000000..a2634a8bf --- /dev/null +++ b/test/syscalls/linux/bad.cc @@ -0,0 +1,39 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(BadSyscallTest, NotImplemented) { + // get_kernel_syms is not supported in Linux > 2.6, and not implemented in + // gVisor. + EXPECT_THAT(syscall(SYS_get_kernel_syms), SyscallFailsWithErrno(ENOSYS)); +} + +TEST(BadSyscallTest, NegativeOne) { + EXPECT_THAT(syscall(-1), SyscallFailsWithErrno(ENOSYS)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/base_poll_test.cc b/test/syscalls/linux/base_poll_test.cc new file mode 100644 index 000000000..bba0108ea --- /dev/null +++ b/test/syscalls/linux/base_poll_test.cc @@ -0,0 +1,65 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/base_poll_test.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +static volatile int timer_fired = 0; +static void SigAlarmHandler(int, siginfo_t*, void*) { timer_fired = 1; } + +BasePollTest::BasePollTest() { + // Register our SIGALRM handler, but save the original so we can restore in + // the destructor. + struct sigaction sa = {}; + sa.sa_sigaction = SigAlarmHandler; + sigfillset(&sa.sa_mask); + TEST_PCHECK(sigaction(SIGALRM, &sa, &original_alarm_sa_) == 0); +} + +BasePollTest::~BasePollTest() { + ClearTimer(); + TEST_PCHECK(sigaction(SIGALRM, &original_alarm_sa_, nullptr) == 0); +} + +void BasePollTest::SetTimer(absl::Duration duration) { + pid_t tgid = getpid(); + pid_t tid = gettid(); + ClearTimer(); + + // Create a new timer thread. + timer_ = absl::make_unique(absl::Now() + duration, tgid, tid); +} + +bool BasePollTest::TimerFired() const { return timer_fired; } + +void BasePollTest::ClearTimer() { + timer_.reset(); + timer_fired = 0; +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/base_poll_test.h b/test/syscalls/linux/base_poll_test.h new file mode 100644 index 000000000..9b9b81933 --- /dev/null +++ b/test/syscalls/linux/base_poll_test.h @@ -0,0 +1,101 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_BASE_POLL_TEST_H_ +#define GVISOR_TEST_SYSCALLS_BASE_POLL_TEST_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +// TimerThread is a cancelable timer. +class TimerThread { + public: + TimerThread(absl::Time deadline, pid_t tgid, pid_t tid) + : thread_([=] { + mu_.Lock(); + mu_.AwaitWithDeadline(absl::Condition(&cancel_), deadline); + if (!cancel_) { + TEST_PCHECK(tgkill(tgid, tid, SIGALRM) == 0); + } + mu_.Unlock(); + }) {} + + ~TimerThread() { Cancel(); } + + void Cancel() { + absl::MutexLock ml(&mu_); + cancel_ = true; + } + + private: + mutable absl::Mutex mu_; + bool cancel_ GUARDED_BY(mu_) = false; + + // Must be last to ensure that the destructor for the thread is run before + // any other member of the object is destroyed. + ScopedThread thread_; +}; + +// Base test fixture for poll, select, ppoll, and pselect tests. +// +// This fixture makes use of SIGALRM. The handler is saved in SetUp() and +// restored in TearDown(). +class BasePollTest : public ::testing::Test { + protected: + BasePollTest(); + ~BasePollTest() override; + + // Sets a timer that will send a signal to the calling thread after + // `duration`. + void SetTimer(absl::Duration duration); + + // Returns true if the timer has fired. + bool TimerFired() const; + + // Stops the pending timer (if any) and clear the "fired" state. + void ClearTimer(); + + private: + // Thread that implements the timer. If the timer is stopped, timer_ is null. + // + // We have to use a thread for this purpose because tests using this fixture + // expect to be interrupted by the timer signal, but itimers/alarm(2) send + // thread-group-directed signals, which may be handled by any thread in the + // test process. + std::unique_ptr timer_; + + // The original SIGALRM handler, to restore in destructor. + struct sigaction original_alarm_sa_; +}; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_BASE_POLL_TEST_H_ diff --git a/test/syscalls/linux/bind.cc b/test/syscalls/linux/bind.cc new file mode 100644 index 000000000..354e8e53c --- /dev/null +++ b/test/syscalls/linux/bind.cc @@ -0,0 +1,146 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST_P(AllSocketPairTest, Bind) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, BindTooLong) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + // first_addr is a sockaddr_storage being used as a sockaddr_un. Use the full + // length which is longer than expected for a Unix socket. + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sizeof(sockaddr_storage)), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, DoubleBindSocket) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + EXPECT_THAT( + bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + // Linux 4.09 returns EINVAL here, but some time before 4.19 it switched + // to EADDRINUSE. + AnyOf(SyscallFailsWithErrno(EADDRINUSE), SyscallFailsWithErrno(EINVAL))); +} + +TEST_P(AllSocketPairTest, GetLocalAddr) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + socklen_t addressLength = sockets->first_addr_size(); + struct sockaddr_storage address = {}; + ASSERT_THAT(getsockname(sockets->first_fd(), (struct sockaddr*)(&address), + &addressLength), + SyscallSucceeds()); + EXPECT_EQ( + 0, memcmp(&address, sockets->first_addr(), sockets->first_addr_size())); +} + +TEST_P(AllSocketPairTest, GetLocalAddrWithoutBind) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + socklen_t addressLength = sockets->first_addr_size(); + struct sockaddr_storage received_address = {}; + ASSERT_THAT( + getsockname(sockets->first_fd(), (struct sockaddr*)(&received_address), + &addressLength), + SyscallSucceeds()); + struct sockaddr_storage want_address = {}; + want_address.ss_family = sockets->first_addr()->sa_family; + EXPECT_EQ(0, memcmp(&received_address, &want_address, addressLength)); +} + +TEST_P(AllSocketPairTest, GetRemoteAddressWithoutConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + socklen_t addressLength = sockets->first_addr_size(); + struct sockaddr_storage address = {}; + ASSERT_THAT(getpeername(sockets->second_fd(), (struct sockaddr*)(&address), + &addressLength), + SyscallFailsWithErrno(ENOTCONN)); +} + +TEST_P(AllSocketPairTest, DoubleBindAddress) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + EXPECT_THAT(bind(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallFailsWithErrno(EADDRINUSE)); +} + +TEST_P(AllSocketPairTest, Unbind) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + + // Filesystem Unix sockets do not release their address when closed. + if (sockets->first_addr()->sa_data[0] != 0) { + ASSERT_THAT(bind(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallFailsWithErrno(EADDRINUSE)); + return; + } + + ASSERT_THAT(bind(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(VecCat( + ApplyVec( + FilesystemUnboundUnixDomainSocketPair, + AllBitwiseCombinations( + List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractUnboundUnixDomainSocketPair, + AllBitwiseCombinations( + List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, List{0, SOCK_CLOEXEC}))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/brk.cc b/test/syscalls/linux/brk.cc new file mode 100644 index 000000000..33d353959 --- /dev/null +++ b/test/syscalls/linux/brk.cc @@ -0,0 +1,31 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +TEST(BrkTest, BrkSyscallReturnsOldBrkOnFailure) { + auto old_brk = sbrk(0); + EXPECT_THAT(syscall(SYS_brk, reinterpret_cast(-1)), + SyscallSucceedsWithValue(reinterpret_cast(old_brk))); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/chdir.cc b/test/syscalls/linux/chdir.cc new file mode 100644 index 000000000..4905ffb23 --- /dev/null +++ b/test/syscalls/linux/chdir.cc @@ -0,0 +1,69 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(ChdirTest, Success) { + auto old_dir = GetAbsoluteTestTmpdir(); + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(chdir(temp_dir.path().c_str()), SyscallSucceeds()); + // Temp path destructor deletes the newly created tmp dir and Sentry rejects + // saving when its current dir is still pointing to the path. Switch to a + // permanent path here. + EXPECT_THAT(chdir(old_dir.c_str()), SyscallSucceeds()); +} + +TEST(ChdirTest, PermissionDenied) { + // Drop capabilities that allow us to override directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); + EXPECT_THAT(chdir(temp_dir.path().c_str()), SyscallFailsWithErrno(EACCES)); +} + +TEST(ChdirTest, NotDir) { + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + EXPECT_THAT(chdir(temp_file.path().c_str()), SyscallFailsWithErrno(ENOTDIR)); +} + +TEST(ChdirTest, NameTooLong) { + std::string name(NAME_MAX + 1, 'a'); + ASSERT_THAT(chdir(name.c_str()), SyscallFailsWithErrno(ENAMETOOLONG)); +} + +TEST(ChdirTest, NotExist) { + EXPECT_THAT(chdir("/foo/bar"), SyscallFailsWithErrno(ENOENT)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/chmod.cc b/test/syscalls/linux/chmod.cc new file mode 100644 index 000000000..b7fc17946 --- /dev/null +++ b/test/syscalls/linux/chmod.cc @@ -0,0 +1,262 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(ChmodTest, ChmodFileSucceeds) { + // Drop capabilities that allow us to override file permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + ASSERT_THAT(chmod(file.path().c_str(), 0466), SyscallSucceeds()); + EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, ChmodDirSucceeds) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string fileInDir = NewTempAbsPathInDir(dir.path()); + + ASSERT_THAT(chmod(dir.path().c_str(), 0466), SyscallSucceeds()); + EXPECT_THAT(open(fileInDir.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, FchmodFileSucceeds_NoRandomSave) { + // Drop capabilities that allow us to file directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); + int fd; + ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds()); + + { + const DisableSave ds; // File permissions are reduced. + ASSERT_THAT(fchmod(fd, 0444), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + } + + EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, FchmodDirSucceeds_NoRandomSave) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + int fd; + ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), + SyscallSucceeds()); + + { + const DisableSave ds; // File permissions are reduced. + ASSERT_THAT(fchmod(fd, 0), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + } + + EXPECT_THAT(open(dir.path().c_str(), O_RDONLY), + SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, FchmodBadF) { + ASSERT_THAT(fchmod(-1, 0444), SyscallFailsWithErrno(EBADF)); +} + +TEST(ChmodTest, FchmodatBadF) { + ASSERT_THAT(fchmodat(-1, "foo", 0444, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST(ChmodTest, FchmodatNotDir) { + ASSERT_THAT(fchmodat(-1, "", 0444, 0), SyscallFailsWithErrno(ENOENT)); +} + +TEST(ChmodTest, FchmodatFileAbsolutePath) { + // Drop capabilities that allow us to override file permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + ASSERT_THAT(fchmodat(-1, file.path().c_str(), 0444, 0), SyscallSucceeds()); + EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, FchmodatDirAbsolutePath) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + int fd; + ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + ASSERT_THAT(fchmodat(-1, dir.path().c_str(), 0, 0), SyscallSucceeds()); + EXPECT_THAT(open(dir.path().c_str(), O_RDONLY), + SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, FchmodatFile) { + // Drop capabilities that allow us to override file permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + int parent_fd; + ASSERT_THAT( + parent_fd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY | O_DIRECTORY), + SyscallSucceeds()); + + ASSERT_THAT( + fchmodat(parent_fd, std::string(Basename(temp_file.path())).c_str(), 0444, 0), + SyscallSucceeds()); + EXPECT_THAT(close(parent_fd), SyscallSucceeds()); + + EXPECT_THAT(open(temp_file.path().c_str(), O_RDWR), + SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, FchmodatDir) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + int parent_fd; + ASSERT_THAT( + parent_fd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY | O_DIRECTORY), + SyscallSucceeds()); + + int fd; + ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + ASSERT_THAT(fchmodat(parent_fd, std::string(Basename(dir.path())).c_str(), 0, 0), + SyscallSucceeds()); + EXPECT_THAT(close(parent_fd), SyscallSucceeds()); + + EXPECT_THAT(open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), + SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, ChmodDowngradeWritability_NoRandomSave) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); + + int fd; + ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds()); + + const DisableSave ds; // Permissions are dropped. + ASSERT_THAT(chmod(file.path().c_str(), 0444), SyscallSucceeds()); + EXPECT_THAT(write(fd, "hello", 5), SyscallSucceedsWithValue(5)); + + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(ChmodTest, ChmodFileToNoPermissionsSucceeds) { + // Drop capabilities that allow us to override file permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); + + ASSERT_THAT(chmod(file.path().c_str(), 0), SyscallSucceeds()); + + EXPECT_THAT(open(file.path().c_str(), O_RDONLY), + SyscallFailsWithErrno(EACCES)); +} + +TEST(ChmodTest, FchmodDowngradeWritability_NoRandomSave) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + int fd; + ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + + const DisableSave ds; // Permissions are dropped. + ASSERT_THAT(fchmod(fd, 0444), SyscallSucceeds()); + EXPECT_THAT(write(fd, "hello", 5), SyscallSucceedsWithValue(5)); + + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(ChmodTest, FchmodFileToNoPermissionsSucceeds_NoRandomSave) { + // Drop capabilities that allow us to override file permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); + + int fd; + ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds()); + + { + const DisableSave ds; // Permissions are dropped. + ASSERT_THAT(fchmod(fd, 0), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + } + + EXPECT_THAT(open(file.path().c_str(), O_RDONLY), + SyscallFailsWithErrno(EACCES)); +} + +// Verify that we can get a RW FD after chmod, even if a RO fd is left open. +TEST(ChmodTest, ChmodWritableWithOpenFD) { + // FIXME: broken on hostfs. + if (IsRunningOnGvisor()) { + return; + } + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0444)); + + FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + ASSERT_THAT(fchmod(fd1.get(), 0644), SyscallSucceeds()); + + // This FD is writable, even though fd1 has a read-only reference to the file. + FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); + + // fd1 is not writable, but fd2 is. + char c = 'a'; + EXPECT_THAT(WriteFd(fd1.get(), &c, 1), SyscallFailsWithErrno(EBADF)); + EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/chown.cc b/test/syscalls/linux/chown.cc new file mode 100644 index 000000000..aa1df05b1 --- /dev/null +++ b/test/syscalls/linux/chown.cc @@ -0,0 +1,200 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_int32(scratch_uid1, 65534, "first scratch UID"); +DEFINE_int32(scratch_uid2, 65533, "second scratch UID"); +DEFINE_int32(scratch_gid, 65534, "first scratch GID"); + +namespace gvisor { +namespace testing { + +namespace { + +TEST(ChownTest, FchownBadF) { + ASSERT_THAT(fchown(-1, 0, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST(ChownTest, FchownatBadF) { + ASSERT_THAT(fchownat(-1, "fff", 0, 0, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST(ChownTest, FchownatEmptyPath) { + const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const auto fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_RDONLY)); + ASSERT_THAT(fchownat(fd.get(), "", 0, 0, 0), SyscallFailsWithErrno(ENOENT)); +} + +using Chown = + std::function; + +class ChownParamTest : public ::testing::TestWithParam {}; + +TEST_P(ChownParamTest, ChownFileSucceeds) { + if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_CHOWN))) { + ASSERT_NO_ERRNO(SetCapability(CAP_CHOWN, false)); + } + + const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // At least *try* setting to a group other than the EGID. + gid_t gid; + EXPECT_THAT(gid = getegid(), SyscallSucceeds()); + int num_groups; + EXPECT_THAT(num_groups = getgroups(0, nullptr), SyscallSucceeds()); + if (num_groups > 0) { + std::vector list(num_groups); + EXPECT_THAT(getgroups(list.size(), list.data()), SyscallSucceeds()); + gid = list[0]; + } + + EXPECT_NO_ERRNO(GetParam()(file.path(), geteuid(), gid)); + + struct stat s = {}; + ASSERT_THAT(stat(file.path().c_str(), &s), SyscallSucceeds()); + EXPECT_EQ(s.st_uid, geteuid()); + EXPECT_EQ(s.st_gid, gid); +} + +TEST_P(ChownParamTest, ChownFilePermissionDenied) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + + const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0777)); + + // Drop privileges and change IDs only in child thread, or else this parent + // thread won't be able to open some log files after the test ends. + ScopedThread([&] { + // Drop privileges. + if (HaveCapability(CAP_CHOWN).ValueOrDie()) { + EXPECT_NO_ERRNO(SetCapability(CAP_CHOWN, false)); + } + + // Change EUID and EGID. + // + // See note about POSIX below. + EXPECT_THAT(syscall(SYS_setresgid, -1, FLAGS_scratch_gid, -1), + SyscallSucceeds()); + EXPECT_THAT(syscall(SYS_setresuid, -1, FLAGS_scratch_uid1, -1), + SyscallSucceeds()); + + EXPECT_THAT(GetParam()(file.path(), geteuid(), getegid()), + PosixErrorIs(EPERM, ::testing::ContainsRegex("chown"))); + }); +} + +TEST_P(ChownParamTest, ChownFileSucceedsAsRoot) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_CHOWN)))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_SETUID)))); + + const std::string filename = NewTempAbsPath(); + + absl::Notification fileCreated, fileChowned; + // Change UID only in child thread, or else this parent thread won't be able + // to open some log files after the test ends. + ScopedThread t([&] { + // POSIX requires that all threads in a process share the same UIDs, so + // the NPTL setresuid wrappers use signals to make all threads execute the + // setresuid syscall. However, we want this thread to have its own set of + // credentials different from the parent process, so we use the raw + // syscall. + EXPECT_THAT(syscall(SYS_setresuid, -1, FLAGS_scratch_uid2, -1), + SyscallSucceeds()); + + // Create file and immediately close it. + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0644)); + fd.reset(); // Close the fd. + + fileCreated.Notify(); + fileChowned.WaitForNotification(); + + EXPECT_THAT(open(filename.c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); + FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDONLY)); + }); + + fileCreated.WaitForNotification(); + + // Set file's owners to someone different. + EXPECT_NO_ERRNO(GetParam()(filename, FLAGS_scratch_uid1, FLAGS_scratch_gid)); + + struct stat s; + EXPECT_THAT(stat(filename.c_str(), &s), SyscallSucceeds()); + EXPECT_EQ(s.st_uid, FLAGS_scratch_uid1); + EXPECT_EQ(s.st_gid, FLAGS_scratch_gid); + + fileChowned.Notify(); +} + +PosixError errorFromReturn(const std::string& name, int ret) { + if (ret == -1) { + return PosixError(errno, absl::StrCat(name, " failed")); + } + return NoError(); +} + +INSTANTIATE_TEST_CASE_P( + ChownKinds, ChownParamTest, + ::testing::Values( + [](const std::string& path, uid_t owner, gid_t group) -> PosixError { + int rc = chown(path.c_str(), owner, group); + MaybeSave(); + return errorFromReturn("chown", rc); + }, + [](const std::string& path, uid_t owner, gid_t group) -> PosixError { + int rc = lchown(path.c_str(), owner, group); + MaybeSave(); + return errorFromReturn("lchown", rc); + }, + [](const std::string& path, uid_t owner, gid_t group) -> PosixError { + ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, O_RDWR)); + int rc = fchown(fd.get(), owner, group); + MaybeSave(); + return errorFromReturn("fchown", rc); + }, + [](const std::string& path, uid_t owner, gid_t group) -> PosixError { + ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, O_RDWR)); + int rc = fchownat(fd.get(), "", owner, group, AT_EMPTY_PATH); + MaybeSave(); + return errorFromReturn("fchownat-fd", rc); + }, + [](const std::string& path, uid_t owner, gid_t group) -> PosixError { + ASSIGN_OR_RETURN_ERRNO( + auto dirfd, Open(std::string(Dirname(path)), O_DIRECTORY | O_RDONLY)); + int rc = fchownat(dirfd.get(), std::string(Basename(path)).c_str(), owner, + group, 0); + MaybeSave(); + return errorFromReturn("fchownat-dirfd", rc); + })); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/chroot.cc b/test/syscalls/linux/chroot.cc new file mode 100644 index 000000000..f921f9025 --- /dev/null +++ b/test/syscalls/linux/chroot.cc @@ -0,0 +1,364 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "test/util/capability_util.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/mount_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +using ::testing::HasSubstr; +using ::testing::Not; + +namespace gvisor { +namespace testing { + +namespace { + +TEST(ChrootTest, Success) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds()); +} + +TEST(ChrootTest, PermissionDenied) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + // CAP_DAC_READ_SEARCH and CAP_DAC_OVERRIDE may override Execute permission on + // directories. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); + EXPECT_THAT(chroot(temp_dir.path().c_str()), SyscallFailsWithErrno(EACCES)); +} + +TEST(ChrootTest, NotDir) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + EXPECT_THAT(chroot(temp_file.path().c_str()), SyscallFailsWithErrno(ENOTDIR)); +} + +TEST(ChrootTest, NotExist) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + EXPECT_THAT(chroot("/foo/bar"), SyscallFailsWithErrno(ENOENT)); +} + +TEST(ChrootTest, WithoutCapability) { + // Unset CAP_SYS_CHROOT. + ASSERT_NO_ERRNO(SetCapability(CAP_SYS_CHROOT, false)); + + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(chroot(temp_dir.path().c_str()), SyscallFailsWithErrno(EPERM)); +} + +TEST(ChrootTest, CreatesNewRoot) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + // Grab the initial cwd. + char initial_cwd[1024]; + ASSERT_THAT(syscall(__NR_getcwd, initial_cwd, sizeof(initial_cwd)), + SyscallSucceeds()); + + auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto file_in_new_root = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(new_root.path())); + + // chroot into new_root. + ASSERT_THAT(chroot(new_root.path().c_str()), SyscallSucceeds()); + + // getcwd should return "(unreachable)" followed by the initial_cwd. + char cwd[1024]; + ASSERT_THAT(syscall(__NR_getcwd, cwd, sizeof(cwd)), SyscallSucceeds()); + std::string expected_cwd = "(unreachable)"; + expected_cwd += initial_cwd; + EXPECT_STREQ(cwd, expected_cwd.c_str()); + + // Should not be able to stat file by its full path. + struct stat statbuf; + EXPECT_THAT(stat(file_in_new_root.path().c_str(), &statbuf), + SyscallFailsWithErrno(ENOENT)); + + // Should be able to stat file at new rooted path. + auto basename = std::string(Basename(file_in_new_root.path())); + auto rootedFile = "/" + basename; + ASSERT_THAT(stat(rootedFile.c_str(), &statbuf), SyscallSucceeds()); + + // Should be able to stat cwd at '.' even though it's outside root. + ASSERT_THAT(stat(".", &statbuf), SyscallSucceeds()); + + // chdir into new root. + ASSERT_THAT(chdir("/"), SyscallSucceeds()); + + // getcwd should return "/". + EXPECT_THAT(syscall(__NR_getcwd, cwd, sizeof(cwd)), SyscallSucceeds()); + EXPECT_STREQ(cwd, "/"); + + // Statting '.', '..', '/', and '/..' all return the same dev and inode. + struct stat statbuf_dot; + ASSERT_THAT(stat(".", &statbuf_dot), SyscallSucceeds()); + struct stat statbuf_dotdot; + ASSERT_THAT(stat("..", &statbuf_dotdot), SyscallSucceeds()); + EXPECT_EQ(statbuf_dot.st_dev, statbuf_dotdot.st_dev); + EXPECT_EQ(statbuf_dot.st_ino, statbuf_dotdot.st_ino); + struct stat statbuf_slash; + ASSERT_THAT(stat("/", &statbuf_slash), SyscallSucceeds()); + EXPECT_EQ(statbuf_dot.st_dev, statbuf_slash.st_dev); + EXPECT_EQ(statbuf_dot.st_ino, statbuf_slash.st_ino); + struct stat statbuf_slashdotdot; + ASSERT_THAT(stat("/..", &statbuf_slashdotdot), SyscallSucceeds()); + EXPECT_EQ(statbuf_dot.st_dev, statbuf_slashdotdot.st_dev); + EXPECT_EQ(statbuf_dot.st_ino, statbuf_slashdotdot.st_ino); +} + +TEST(ChrootTest, DotDotFromOpenFD) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + auto dir_outside_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(dir_outside_root.path(), O_RDONLY | O_DIRECTORY)); + auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + // chroot into new_root. + ASSERT_THAT(chroot(new_root.path().c_str()), SyscallSucceeds()); + + // openat on fd with path .. will succeed. + int other_fd; + ASSERT_THAT(other_fd = openat(fd.get(), "..", O_RDONLY), SyscallSucceeds()); + EXPECT_THAT(close(other_fd), SyscallSucceeds()); + + // getdents on fd should not error. + char buf[1024]; + ASSERT_THAT(syscall(SYS_getdents, fd.get(), buf, sizeof(buf)), + SyscallSucceeds()); +} + +// Test that link resolution in a chroot can escape the root by following an +// open proc fd. +TEST(ChrootTest, ProcFdLinkResolutionInChroot) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + const TempPath file_outside_chroot = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file_outside_chroot.path(), O_RDONLY)); + + const FileDescriptor proc_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)); + + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + ASSERT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds()); + + // Opening relative to an already open fd to a node outside the chroot works. + const FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE( + OpenAt(proc_fd.get(), "self/fd", O_DIRECTORY | O_RDONLY | O_CLOEXEC)); + + // Proc fd symlinks can escape the chroot if the fd the symlink refers to + // refers to an object outside the chroot. + struct stat s = {}; + EXPECT_THAT( + fstatat(proc_self_fd.get(), absl::StrCat(fd.get()).c_str(), &s, 0), + SyscallSucceeds()); + + // Try to stat the stdin fd. Internally, this is handled differently from a + // proc fd entry pointing to a file, since stdin is backed by a host fd, and + // isn't a walkable path on the filesystem inside the sandbox. + EXPECT_THAT(fstatat(proc_self_fd.get(), "0", &s, 0), SyscallSucceeds()); +} + +// This test will verify that when you hold a fd to proc before entering +// a chroot that any files inside the chroot will appear rooted to the +// base chroot when examining /proc/self/fd/{num}. +TEST(ChrootTest, ProcMemSelfFdsNoEscapeProcOpen) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + // Get a FD to /proc before we enter the chroot. + const FileDescriptor proc = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY)); + + // Create and enter a chroot directory. + const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + ASSERT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds()); + + // Open a file inside the chroot at /foo. + const FileDescriptor foo = + ASSERT_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644)); + + // Examine /proc/self/fd/{foo_fd} to see if it exposes the fact that we're + // inside a chroot, the path should be /foo and NOT {chroot_dir}/foo. + const std::string fd_path = absl::StrCat("self/fd/", foo.get()); + char buf[1024] = {}; + size_t bytes_read = 0; + ASSERT_THAT(bytes_read = + readlinkat(proc.get(), fd_path.c_str(), buf, sizeof(buf) - 1), + SyscallSucceeds()); + + // The link should resolve to something. + ASSERT_GT(bytes_read, 0); + + // Assert that the link doesn't contain the chroot path and is only /foo. + EXPECT_STREQ(buf, "/foo"); +} + +// This test will verify that a file inside a chroot when mmapped will not +// expose the full file path via /proc/self/maps and instead honor the chroot. +TEST(ChrootTest, ProcMemSelfMapsNoEscapeProcOpen) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); + + // Get a FD to /proc before we enter the chroot. + const FileDescriptor proc = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY)); + + // Create and enter a chroot directory. + const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + ASSERT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds()); + + // Open a file inside the chroot at /foo. + const FileDescriptor foo = + ASSERT_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644)); + + // Mmap the newly created file. + void* foo_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + foo.get(), 0); + ASSERT_THAT(reinterpret_cast(foo_map), SyscallSucceeds()); + + // Always unmap. + auto cleanup_map = Cleanup( + [&] { EXPECT_THAT(munmap(foo_map, kPageSize), SyscallSucceeds()); }); + + // Examine /proc/self/maps to be sure that /foo doesn't appear to be + // mapped with the full chroot path. + const FileDescriptor maps = + ASSERT_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), "self/maps", O_RDONLY)); + + size_t bytes_read = 0; + char buf[8 * 1024] = {}; + ASSERT_THAT(bytes_read = ReadFd(maps.get(), buf, sizeof(buf)), + SyscallSucceeds()); + + // The maps file should have something. + ASSERT_GT(bytes_read, 0); + + // Finally we want to make sure the maps don't contain the chroot path + ASSERT_EQ(std::string(buf, bytes_read).find(temp_dir.path()), std::string::npos); +} + +// Test that mounts outside the chroot will not appear in /proc/self/mounts or +// /proc/self/mountinfo. +TEST(ChrootTest, ProcMountsMountinfoNoEscape) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + // We are going to create some mounts and then chroot. In order to be able to + // unmount the mounts after the test run, we must chdir to the root and use + // relative paths for all mounts. That way, as long as we never chdir into + // the new root, we can access the mounts via relative paths and unmount them. + ASSERT_THAT(chdir("/"), SyscallSucceeds()); + + // Create nested tmpfs mounts. Note the use of relative paths in Mount calls. + auto const outer_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const outer_mount = ASSERT_NO_ERRNO_AND_VALUE(Mount( + "none", JoinPath(".", outer_dir.path()), "tmpfs", 0, "mode=0700", 0)); + + auto const inner_dir = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(outer_dir.path())); + auto const inner_mount = ASSERT_NO_ERRNO_AND_VALUE(Mount( + "none", JoinPath(".", inner_dir.path()), "tmpfs", 0, "mode=0700", 0)); + + // Filenames that will be checked for mounts, all relative to /proc dir. + std::string paths[3] = {"mounts", "self/mounts", "self/mountinfo"}; + + for (const std::string& path : paths) { + // We should have both inner and outer mounts. + const std::string contents = + ASSERT_NO_ERRNO_AND_VALUE(GetContents(JoinPath("/proc", path))); + EXPECT_THAT(contents, AllOf(HasSubstr(outer_dir.path()), + HasSubstr(inner_dir.path()))); + // We better have at least two mounts: the mounts we created plus the root. + std::vector submounts = + absl::StrSplit(contents, '\n', absl::SkipWhitespace()); + EXPECT_GT(submounts.size(), 2); + } + + // Get a FD to /proc before we enter the chroot. + const FileDescriptor proc = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY)); + + // Chroot to outer mount. + ASSERT_THAT(chroot(outer_dir.path().c_str()), SyscallSucceeds()); + + for (const std::string& path : paths) { + const FileDescriptor proc_file = + ASSERT_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY)); + + // Only two mounts visible from this chroot: the inner and outer. Both + // paths should be relative to the new chroot. + const std::string contents = + ASSERT_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get())); + EXPECT_THAT(contents, + AllOf(HasSubstr(absl::StrCat(Basename(inner_dir.path()))), + Not(HasSubstr(outer_dir.path())), + Not(HasSubstr(inner_dir.path())))); + std::vector submounts = + absl::StrSplit(contents, '\n', absl::SkipWhitespace()); + EXPECT_EQ(submounts.size(), 2); + } + + // Chroot to inner mount. We must use an absolute path accessible to our + // chroot. + const std::string inner_dir_basename = + absl::StrCat("/", Basename(inner_dir.path())); + ASSERT_THAT(chroot(inner_dir_basename.c_str()), SyscallSucceeds()); + + for (const std::string& path : paths) { + const FileDescriptor proc_file = + ASSERT_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY)); + const std::string contents = + ASSERT_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get())); + + // Only the inner mount visible from this chroot. + std::vector submounts = + absl::StrSplit(contents, '\n', absl::SkipWhitespace()); + EXPECT_EQ(submounts.size(), 1); + } + + // Chroot back to ".". + ASSERT_THAT(chroot("."), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/clock_getres.cc b/test/syscalls/linux/clock_getres.cc new file mode 100644 index 000000000..8f8842299 --- /dev/null +++ b/test/syscalls/linux/clock_getres.cc @@ -0,0 +1,37 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// clock_getres works regardless of whether or not a timespec is passed. +TEST(ClockGetres, Timespec) { + struct timespec ts; + EXPECT_THAT(clock_getres(CLOCK_MONOTONIC, &ts), SyscallSucceeds()); + EXPECT_THAT(clock_getres(CLOCK_MONOTONIC, nullptr), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/clock_gettime.cc b/test/syscalls/linux/clock_gettime.cc new file mode 100644 index 000000000..5003928be --- /dev/null +++ b/test/syscalls/linux/clock_gettime.cc @@ -0,0 +1,156 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +int64_t clock_gettime_nsecs(clockid_t id) { + struct timespec ts; + TEST_PCHECK(clock_gettime(id, &ts) == 0); + return (ts.tv_sec * 1000000000 + ts.tv_nsec); +} + +// Spin on the CPU for at least ns nanoseconds, based on +// CLOCK_THREAD_CPUTIME_ID. +void spin_ns(int64_t ns) { + int64_t start = clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID); + int64_t end = start + ns; + + do { + constexpr int kLoopCount = 1000000; // large and arbitrary + // volatile to prevent the compiler from skipping this loop. + for (volatile int i = 0; i < kLoopCount; i++) { + } + } while (clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID) < end); +} + +// Test that CLOCK_PROCESS_CPUTIME_ID is a superset of CLOCK_THREAD_CPUTIME_ID. +TEST(ClockGettime, CputimeId) { + constexpr int kNumThreads = 13; // arbitrary + + absl::Duration spin_time = absl::Seconds(1); + + // Start off the worker threads and compute the aggregate time spent by + // the workers. Note that we test CLOCK_PROCESS_CPUTIME_ID by having the + // workers execute in parallel and verifying that CLOCK_PROCESS_CPUTIME_ID + // accumulates the runtime of all threads. + int64_t start = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID); + + // Create a kNumThreads threads. + std::list threads; + for (int i = 0; i < kNumThreads; i++) { + threads.emplace_back( + [spin_time] { spin_ns(absl::ToInt64Nanoseconds(spin_time)); }); + } + for (auto& t : threads) { + t.Join(); + } + + int64_t end = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID); + + // The aggregate time spent in the worker threads must be at least + // 'kNumThreads' times the time each thread spun. + ASSERT_GE(end - start, kNumThreads * absl::ToInt64Nanoseconds(spin_time)); +} + +TEST(ClockGettime, JavaThreadTime) { + clockid_t clockid; + ASSERT_EQ(0, pthread_getcpuclockid(pthread_self(), &clockid)); + struct timespec tp; + ASSERT_THAT(clock_getres(clockid, &tp), SyscallSucceeds()); + ASSERT_THAT(clock_gettime(clockid, &tp), SyscallSucceeds()); + EXPECT_TRUE(tp.tv_sec > 0 || tp.tv_nsec > 0); +} + +// There is not much to test here, since CLOCK_REALTIME may be discontiguous. +TEST(ClockGettime, RealtimeWorks) { + struct timespec tp; + EXPECT_THAT(clock_gettime(CLOCK_REALTIME, &tp), SyscallSucceeds()); +} + +class MonotonicClockTest : public ::testing::TestWithParam {}; + +TEST_P(MonotonicClockTest, IsMonotonic) { + auto end = absl::Now() + absl::Seconds(5); + + struct timespec tp; + EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds()); + + auto prev = absl::TimeFromTimespec(tp); + while (absl::Now() < end) { + EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds()); + auto now = absl::TimeFromTimespec(tp); + EXPECT_GE(now, prev); + prev = now; + } +} + +std::string PrintClockId(::testing::TestParamInfo info) { + switch (info.param) { + case CLOCK_MONOTONIC: + return "CLOCK_MONOTONIC"; + case CLOCK_MONOTONIC_COARSE: + return "CLOCK_MONOTONIC_COARSE"; + case CLOCK_MONOTONIC_RAW: + return "CLOCK_MONOTONIC_RAW"; + default: + return absl::StrCat(info.param); + } +} + +INSTANTIATE_TEST_CASE_P(ClockGettime, MonotonicClockTest, + ::testing::Values(CLOCK_MONOTONIC, + CLOCK_MONOTONIC_COARSE, + CLOCK_MONOTONIC_RAW), + PrintClockId); + +TEST(ClockGettime, UnimplementedReturnsEINVAL) { + SKIP_IF(!IsRunningOnGvisor()); + + struct timespec tp; + EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME, &tp), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(clock_gettime(CLOCK_REALTIME_ALARM, &tp), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME_ALARM, &tp), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(ClockGettime, InvalidClockIDReturnsEINVAL) { + struct timespec tp; + EXPECT_THAT(clock_gettime(-1, &tp), SyscallFailsWithErrno(EINVAL)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/clock_nanosleep.cc b/test/syscalls/linux/clock_nanosleep.cc new file mode 100644 index 000000000..96bb961b4 --- /dev/null +++ b/test/syscalls/linux/clock_nanosleep.cc @@ -0,0 +1,153 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/time.h" +#include "test/util/cleanup.h" +#include "test/util/posix_error.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// sys_clock_nanosleep is defined because the glibc clock_nanosleep returns +// error numbers directly and does not set errno. This makes our Syscall +// matchers look a little weird when expecting failure: +// "SyscallSucceedsWithValue(ERRNO)". +int sys_clock_nanosleep(clockid_t clkid, int flags, + const struct timespec* request, + struct timespec* remain) { + return syscall(SYS_clock_nanosleep, clkid, flags, request, remain); +} + +PosixErrorOr GetTime(clockid_t clk) { + struct timespec ts = {}; + int rc = clock_gettime(clk, &ts); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "clock_gettime"); + } + return absl::TimeFromTimespec(ts); +} + +class WallClockNanosleepTest : public ::testing::TestWithParam {}; + +TEST_P(WallClockNanosleepTest, InvalidValues) { + const struct timespec invalid[] = { + {.tv_sec = -1, .tv_nsec = -1}, {.tv_sec = 0, .tv_nsec = INT32_MIN}, + {.tv_sec = 0, .tv_nsec = INT32_MAX}, {.tv_sec = 0, .tv_nsec = -1}, + {.tv_sec = -1, .tv_nsec = 0}, + }; + + for (auto const ts : invalid) { + EXPECT_THAT(sys_clock_nanosleep(GetParam(), 0, &ts, nullptr), + SyscallFailsWithErrno(EINVAL)); + } +} + +TEST_P(WallClockNanosleepTest, SleepOneSecond) { + absl::Duration const duration = absl::Seconds(1); + struct timespec dur = absl::ToTimespec(duration); + + absl::Time const before = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); + EXPECT_THAT(RetryEINTR(sys_clock_nanosleep)(GetParam(), 0, &dur, &dur), + SyscallSucceeds()); + absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); + + EXPECT_GE(after - before, duration); +} + +TEST_P(WallClockNanosleepTest, InterruptedNanosleep) { + absl::Duration const duration = absl::Seconds(60); + struct timespec dur = absl::ToTimespec(duration); + + // Install no-op signal handler for SIGALRM. + struct sigaction sa = {}; + sigfillset(&sa.sa_mask); + sa.sa_handler = +[](int signo) {}; + auto const cleanup_sa = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Measure time since setting the alarm, since the alarm will interrupt the + // sleep and hence determine how long we sleep. + absl::Time const before = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); + + // Set an alarm to go off while sleeping. + struct itimerval timer = {}; + timer.it_value.tv_sec = 1; + timer.it_value.tv_usec = 0; + timer.it_interval.tv_sec = 1; + timer.it_interval.tv_usec = 0; + auto const cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, timer)); + + EXPECT_THAT(sys_clock_nanosleep(GetParam(), 0, &dur, &dur), + SyscallFailsWithErrno(EINTR)); + absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); + + absl::Duration const remaining = absl::DurationFromTimespec(dur); + EXPECT_GE(after - before + remaining, duration); +} + +TEST_P(WallClockNanosleepTest, SleepUntil) { + absl::Time const now = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); + absl::Time const until = now + absl::Seconds(2); + struct timespec ts = absl::ToTimespec(until); + + EXPECT_THAT( + RetryEINTR(sys_clock_nanosleep)(GetParam(), TIMER_ABSTIME, &ts, nullptr), + SyscallSucceeds()); + absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); + + EXPECT_GE(after, until); +} + +INSTANTIATE_TEST_CASE_P(Sleepers, WallClockNanosleepTest, + ::testing::Values(CLOCK_REALTIME, CLOCK_MONOTONIC)); + +TEST(ClockNanosleepProcessTest, SleepFiveSeconds) { + absl::Duration const kDuration = absl::Seconds(5); + struct timespec dur = absl::ToTimespec(kDuration); + + // Ensure that CLOCK_PROCESS_CPUTIME_ID advances. + std::atomic done(false); + ScopedThread t([&] { + while (!done.load()) { + } + }); + auto const cleanup_done = Cleanup([&] { done.store(true); }); + + absl::Time const before = + ASSERT_NO_ERRNO_AND_VALUE(GetTime(CLOCK_PROCESS_CPUTIME_ID)); + EXPECT_THAT( + RetryEINTR(sys_clock_nanosleep)(CLOCK_PROCESS_CPUTIME_ID, 0, &dur, &dur), + SyscallSucceeds()); + absl::Time const after = + ASSERT_NO_ERRNO_AND_VALUE(GetTime(CLOCK_PROCESS_CPUTIME_ID)); + EXPECT_GE(after - before, kDuration); +} +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/concurrency.cc b/test/syscalls/linux/concurrency.cc new file mode 100644 index 000000000..2c13b315c --- /dev/null +++ b/test/syscalls/linux/concurrency.cc @@ -0,0 +1,124 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { +namespace { + +// Test that a thread that never yields to the OS does not prevent other threads +// from running. +TEST(ConcurrencyTest, SingleProcessMultithreaded) { + std::atomic a(0); + + ScopedThread t([&a]() { + while (!a.load()) { + } + }); + + absl::SleepFor(absl::Seconds(1)); + + // We are still able to execute code in this thread. The other hasn't + // permanently hung execution in both threads. + a.store(1); +} + +// Test that multiple threads in this process continue to execute in parallel, +// even if an unrelated second process is spawned. +TEST(ConcurrencyTest, MultiProcessMultithreaded) { + // In PID 1, start TIDs 1 and 2, and put both to sleep. + // + // Start PID 3, which spins for 5 seconds, then exits. + // + // TIDs 1 and 2 wake and attempt to Activate, which cannot occur until PID 3 + // exits. + // + // Both TIDs 1 and 2 should be woken. If they are not both woken, the test + // hangs. + // + // This is all fundamentally racy. If we are failing to wake all threads, the + // expectation is that this test becomes flaky, rather than consistently + // failing. + // + // If additional background threads fail to block, we may never schedule the + // child, at which point this test effectively becomes + // MultiProcessConcurrency. That's not expected to occur. + + std::atomic a(0); + ScopedThread t([&a]() { + // Block so that PID 3 can execute and we can wait on its exit. + absl::SleepFor(absl::Seconds(1)); + while (!a.load()) { + } + }); + + pid_t child_pid; + ASSERT_THAT(child_pid = fork(), SyscallSucceeds()); + if (child_pid == 0) { + // Busy wait without making any blocking syscalls. + auto end = absl::Now() + absl::Seconds(5); + while (absl::Now() < end) { + } + _exit(0); + } + + absl::SleepFor(absl::Seconds(1)); + + // If only TID 1 is woken, thread.Join will hang. + // If only TID 2 is woken, both will hang. + a.store(1); + t.Join(); + + int status = 0; + EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(WEXITSTATUS(status), 0); +} + +// Test that multiple processes can execute concurrently, even if one process +// never yields. +TEST(ConcurrencyTest, MultiProcessConcurrency) { + + pid_t child_pid; + ASSERT_THAT(child_pid = fork(), SyscallSucceeds()); + if (child_pid == 0) { + while (true) { + } + __builtin_unreachable(); + } + + absl::SleepFor(absl::Seconds(5)); + + // We are still able to execute code in this process. The other hasn't + // permanently hung execution in both processes. + ASSERT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); + int status = 0; + + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGKILL); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/creat.cc b/test/syscalls/linux/creat.cc new file mode 100644 index 000000000..72a016b4c --- /dev/null +++ b/test/syscalls/linux/creat.cc @@ -0,0 +1,57 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr int kMode = 0666; + +TEST(CreatTest, CreatCreatesNewFile) { + std::string const path = NewTempAbsPath(); + struct stat buf; + int fd; + ASSERT_THAT(stat(path.c_str(), &buf), SyscallFailsWithErrno(ENOENT)); + ASSERT_THAT(fd = creat(path.c_str(), kMode), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + EXPECT_THAT(stat(path.c_str(), &buf), SyscallSucceeds()); +} + +TEST(CreatTest, CreatTruncatesExistingFile) { + auto temp_path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + int fd; + ASSERT_NO_ERRNO(SetContents(temp_path.path(), "non-empty")); + ASSERT_THAT(fd = creat(temp_path.path().c_str(), kMode), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + std::string new_contents; + ASSERT_NO_ERRNO(GetContents(temp_path.path(), &new_contents)); + EXPECT_EQ("", new_contents); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/dev.cc b/test/syscalls/linux/dev.cc new file mode 100644 index 000000000..a140d3b30 --- /dev/null +++ b/test/syscalls/linux/dev.cc @@ -0,0 +1,149 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(DevTest, LseekDevUrandom) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/urandom", O_RDONLY)); + EXPECT_THAT(lseek(fd.get(), -10, SEEK_CUR), SyscallSucceeds()); + EXPECT_THAT(lseek(fd.get(), -10, SEEK_SET), SyscallSucceeds()); + EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); +} + +TEST(DevTest, LseekDevNull) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); + EXPECT_THAT(lseek(fd.get(), -10, SEEK_CUR), SyscallSucceeds()); + EXPECT_THAT(lseek(fd.get(), -10, SEEK_SET), SyscallSucceeds()); + EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); + EXPECT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); +} + +TEST(DevTest, LseekDevZero) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); + EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); + EXPECT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); +} + +TEST(DevTest, LseekDevFull) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/full", O_RDONLY)); + EXPECT_THAT(lseek(fd.get(), 123, SEEK_SET), SyscallSucceedsWithValue(0)); + EXPECT_THAT(lseek(fd.get(), 123, SEEK_CUR), SyscallSucceedsWithValue(0)); + EXPECT_THAT(lseek(fd.get(), 123, SEEK_END), SyscallSucceedsWithValue(0)); +} + +TEST(DevTest, LseekDevNullFreshFile) { + // Seeks to /dev/null always return 0. + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); + const FileDescriptor fd2 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); + + EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + EXPECT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceedsWithValue(0)); + EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + + const FileDescriptor fd3 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); + EXPECT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); +} + +TEST(DevTest, OpenTruncate) { + // Truncation is ignored on linux and gvisor for device files. + ASSERT_NO_ERRNO_AND_VALUE( + Open("/dev/null", O_CREAT | O_TRUNC | O_WRONLY, 0644)); + ASSERT_NO_ERRNO_AND_VALUE( + Open("/dev/zero", O_CREAT | O_TRUNC | O_WRONLY, 0644)); + ASSERT_NO_ERRNO_AND_VALUE( + Open("/dev/full", O_CREAT | O_TRUNC | O_WRONLY, 0644)); +} + +TEST(DevTest, Pread64DevNull) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); + char buf[1]; + EXPECT_THAT(pread64(fd.get(), buf, 1, 0), SyscallSucceedsWithValue(0)); +} + +TEST(DevTest, Pread64DevZero) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); + char buf[1]; + EXPECT_THAT(pread64(fd.get(), buf, 1, 0), SyscallSucceedsWithValue(1)); +} + +TEST(DevTest, Pread64DevFull) { + // /dev/full behaves like /dev/zero with respect to reads. + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/full", O_RDONLY)); + char buf[1]; + EXPECT_THAT(pread64(fd.get(), buf, 1, 0), SyscallSucceedsWithValue(1)); +} + +TEST(DevTest, ReadDevNull) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); + std::vector buf(1); + EXPECT_THAT(ReadFd(fd.get(), buf.data(), 1), SyscallSucceeds()); +} + +// Do not allow random save as it could lead to partial reads. +TEST(DevTest, ReadDevZero_NoRandomSave) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); + + constexpr int kReadSize = 128 * 1024; + std::vector buf(kReadSize, 1); + EXPECT_THAT(ReadFd(fd.get(), buf.data(), kReadSize), + SyscallSucceedsWithValue(kReadSize)); + EXPECT_EQ(std::vector(kReadSize, 0), buf); +} + +TEST(DevTest, WriteDevNull) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_WRONLY)); + EXPECT_THAT(WriteFd(fd.get(), "a", 1), SyscallSucceedsWithValue(1)); +} + +TEST(DevTest, WriteDevZero) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY)); + EXPECT_THAT(WriteFd(fd.get(), "a", 1), SyscallSucceedsWithValue(1)); +} + +TEST(DevTest, WriteDevFull) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/full", O_WRONLY)); + EXPECT_THAT(WriteFd(fd.get(), "a", 1), SyscallFailsWithErrno(ENOSPC)); +} + +} // namespace +} // namespace testing + +} // namespace gvisor diff --git a/test/syscalls/linux/dup.cc b/test/syscalls/linux/dup.cc new file mode 100644 index 000000000..fc11844fb --- /dev/null +++ b/test/syscalls/linux/dup.cc @@ -0,0 +1,139 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +PosixErrorOr Dup2(const FileDescriptor& fd, int target_fd) { + int new_fd = dup2(fd.get(), target_fd); + if (new_fd < 0) { + return PosixError(errno, "Dup2"); + } + return FileDescriptor(new_fd); +} + +PosixErrorOr Dup3(const FileDescriptor& fd, int target_fd, + int flags) { + int new_fd = dup3(fd.get(), target_fd, flags); + if (new_fd < 0) { + return PosixError(errno, "Dup2"); + } + return FileDescriptor(new_fd); +} + +void CheckSameFile(const FileDescriptor& fd1, const FileDescriptor& fd2) { + struct stat stat_result1, stat_result2; + ASSERT_THAT(fstat(fd1.get(), &stat_result1), SyscallSucceeds()); + ASSERT_THAT(fstat(fd2.get(), &stat_result2), SyscallSucceeds()); + EXPECT_EQ(stat_result1.st_dev, stat_result2.st_dev); + EXPECT_EQ(stat_result1.st_ino, stat_result2.st_ino); +} + +TEST(DupTest, Dup) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); + + // Dup the descriptor and make sure it's the same file. + FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); + ASSERT_NE(fd.get(), nfd.get()); + CheckSameFile(fd, nfd); +} + +TEST(DupTest, DupClearsCloExec) { + FileDescriptor nfd; + + // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set. + int event_fd = 0; + ASSERT_THAT(event_fd = eventfd(0, EFD_CLOEXEC), SyscallSucceeds()); + FileDescriptor event_fd_closer(event_fd); + + EXPECT_THAT(fcntl(event_fd_closer.get(), F_GETFD), + SyscallSucceedsWithValue(FD_CLOEXEC)); + + // Duplicate the descriptor. Ensure that it doesn't have FD_CLOEXEC set. + nfd = ASSERT_NO_ERRNO_AND_VALUE(event_fd_closer.Dup()); + ASSERT_NE(event_fd_closer.get(), nfd.get()); + CheckSameFile(event_fd_closer, nfd); + EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); +} + +TEST(DupTest, Dup2) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); + + // Regular dup once. + FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); + + ASSERT_NE(fd.get(), nfd.get()); + CheckSameFile(fd, nfd); + + // Dup over the file above. + int target_fd = nfd.release(); + FileDescriptor nfd2 = ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, target_fd)); + EXPECT_EQ(target_fd, nfd2.get()); + CheckSameFile(fd, nfd2); +} + +TEST(DupTest, Dup2SameFD) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); + + // Should succeed. + ASSERT_THAT(dup2(fd.get(), fd.get()), SyscallSucceedsWithValue(fd.get())); +} + +TEST(DupTest, Dup3) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); + + // Regular dup once. + FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); + ASSERT_NE(fd.get(), nfd.get()); + CheckSameFile(fd, nfd); + + // Dup over the file above, check that it has no CLOEXEC. + nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), 0)); + CheckSameFile(fd, nfd); + EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); + + // Dup over the file again, check that it does not CLOEXEC. + nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), O_CLOEXEC)); + CheckSameFile(fd, nfd); + EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); +} + +TEST(DupTest, Dup3FailsSameFD) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); + + // Only dup3 fails if the new and old fd are the same. + ASSERT_THAT(dup3(fd.get(), fd.get(), 0), SyscallFailsWithErrno(EINVAL)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc new file mode 100644 index 000000000..9ae87c00b --- /dev/null +++ b/test/syscalls/linux/epoll.cc @@ -0,0 +1,468 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr int kFDsPerEpoll = 3; +constexpr uint64_t kMagicConstant = 0x0102030405060708; + +// Returns a new epoll file descriptor. +PosixErrorOr NewEpollFD() { + // "Since Linux 2.6.8, the size argument is ignored, but must be greater than + // zero." - epoll_create(2) + int fd = epoll_create(/* size = */ 1); + MaybeSave(); + if (fd < 0) { + return PosixError(errno, "epoll_create"); + } + return FileDescriptor(fd); +} + +// Returns a new eventfd. +PosixErrorOr NewEventFD() { + int fd = eventfd(/* initval = */ 0, /* flags = */ 0); + MaybeSave(); + if (fd < 0) { + return PosixError(errno, "eventfd"); + } + return FileDescriptor(fd); +} + +// Registers `target_fd` with the epoll instance represented by `epoll_fd` for +// the epoll events `events`. Events on `target_fd` will be indicated by setting +// data.u64 to `data` in the returned epoll_event. +PosixError RegisterEpollFD(int epoll_fd, int target_fd, int events, + uint64_t data) { + struct epoll_event event; + event.events = events; + event.data.u64 = data; + int rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, target_fd, &event); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "epoll_ctl"); + } + return NoError(); +} + +uint64_t ms_elapsed(const struct timespec* begin, const struct timespec* end) { + return (end->tv_sec - begin->tv_sec) * 1000 + + (end->tv_nsec - begin->tv_nsec) / 1000000; +} + +TEST(EpollTest, AllWritable) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + std::vector eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), + EPOLLIN | EPOLLOUT, kMagicConstant + i)); + } + + struct epoll_event result[kFDsPerEpoll]; + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(kFDsPerEpoll)); + // TODO: Why do some tests check epoll_event::data, and others + // don't? Does Linux actually guarantee that, in any of these test cases, + // epoll_wait will necessarily write out the epoll_events in the order that + // they were registered? + for (int i = 0; i < kFDsPerEpoll; i++) { + ASSERT_EQ(result[i].events, EPOLLOUT); + } +} + +TEST(EpollTest, LastReadable) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + std::vector eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), + EPOLLIN | EPOLLOUT, kMagicConstant + i)); + } + + uint64_t tmp = 1; + ASSERT_THAT(WriteFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)), + SyscallSucceedsWithValue(sizeof(tmp))); + + struct epoll_event result[kFDsPerEpoll]; + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(kFDsPerEpoll)); + + int i; + for (i = 0; i < kFDsPerEpoll - 1; i++) { + EXPECT_EQ(result[i].events, EPOLLOUT); + } + EXPECT_EQ(result[i].events, EPOLLOUT | EPOLLIN); + EXPECT_EQ(result[i].data.u64, kMagicConstant + i); +} + +TEST(EpollTest, LastNonWritable) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + std::vector eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), + EPOLLIN | EPOLLOUT, kMagicConstant + i)); + } + + // Write the maximum value to the event fd so that writing to it again would + // block. + uint64_t tmp = ULLONG_MAX - 1; + ASSERT_THAT(WriteFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)), + SyscallSucceedsWithValue(sizeof(tmp))); + + struct epoll_event result[kFDsPerEpoll]; + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(kFDsPerEpoll)); + + int i; + for (i = 0; i < kFDsPerEpoll - 1; i++) { + EXPECT_EQ(result[i].events, EPOLLOUT); + } + EXPECT_EQ(result[i].events, EPOLLIN); + EXPECT_THAT(ReadFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)), + sizeof(tmp)); + EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(kFDsPerEpoll)); + + for (i = 0; i < kFDsPerEpoll; i++) { + EXPECT_EQ(result[i].events, EPOLLOUT); + } +} + +TEST(EpollTest, Timeout_NoRandomSave) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + std::vector eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, + kMagicConstant + i)); + } + + constexpr int kTimeoutMs = 200; + struct timespec begin; + struct timespec end; + struct epoll_event result[kFDsPerEpoll]; + + { + const DisableSave ds; // Timing-related. + EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &begin), SyscallSucceeds()); + + ASSERT_THAT( + RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, kTimeoutMs), + SyscallSucceedsWithValue(0)); + EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &end), SyscallSucceeds()); + } + + // Check the lower bound on the timeout. Checking for an upper bound is + // fragile because Linux can overrun the timeout due to scheduling delays. + EXPECT_GT(ms_elapsed(&begin, &end), kTimeoutMs - 1); +} + +void* writer(void* arg) { + int fd = *reinterpret_cast(arg); + uint64_t tmp = 1; + + usleep(200000); + if (WriteFd(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { + fprintf(stderr, "writer failed: errno %s\n", strerror(errno)); + } + + return nullptr; +} + +TEST(EpollTest, WaitThenUnblock) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + std::vector eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, + kMagicConstant + i)); + } + + // Fire off a thread that will make at least one of the event fds readable. + pthread_t thread; + int make_readable = eventfds[0].get(); + ASSERT_THAT(pthread_create(&thread, nullptr, writer, &make_readable), + SyscallSucceedsWithValue(0)); + + struct epoll_event result[kFDsPerEpoll]; + EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(1)); + EXPECT_THAT(pthread_detach(thread), SyscallSucceeds()); +} + +void sighandler(int s) {} + +void* signaler(void* arg) { + pthread_t* t = reinterpret_cast(arg); + // Repeatedly send the real-time signal until we are detached, because it's + // difficult to know exactly when epoll_wait on another thread (which this + // is intending to interrupt) has started blocking. + while (1) { + usleep(200000); + pthread_kill(*t, SIGRTMIN); + } + return nullptr; +} + +TEST(EpollTest, UnblockWithSignal) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + std::vector eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, + kMagicConstant + i)); + } + + signal(SIGRTMIN, sighandler); + // Unblock the real time signals that InitGoogle blocks :( + sigset_t unblock; + sigemptyset(&unblock); + sigaddset(&unblock, SIGRTMIN); + ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &unblock, nullptr), SyscallSucceeds()); + + pthread_t thread; + pthread_t cur = pthread_self(); + ASSERT_THAT(pthread_create(&thread, nullptr, signaler, &cur), + SyscallSucceedsWithValue(0)); + + struct epoll_event result[kFDsPerEpoll]; + EXPECT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallFailsWithErrno(EINTR)); + EXPECT_THAT(pthread_cancel(thread), SyscallSucceeds()); + EXPECT_THAT(pthread_detach(thread), SyscallSucceeds()); +} + +TEST(EpollTest, TimeoutNoFds) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + struct epoll_event result[kFDsPerEpoll]; + EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), + SyscallSucceedsWithValue(0)); +} + +struct addr_ctx { + int epollfd; + int eventfd; +}; + +void* fd_adder(void* arg) { + struct addr_ctx* actx = reinterpret_cast(arg); + struct epoll_event event; + event.events = EPOLLIN | EPOLLOUT; + event.data.u64 = 0xdeadbeeffacefeed; + + usleep(200000); + if (epoll_ctl(actx->epollfd, EPOLL_CTL_ADD, actx->eventfd, &event) == -1) { + fprintf(stderr, "epoll_ctl failed: %s\n", strerror(errno)); + } + + return nullptr; +} + +TEST(EpollTest, UnblockWithNewFD) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); + + pthread_t thread; + struct addr_ctx actx = {epollfd.get(), eventfd.get()}; + ASSERT_THAT(pthread_create(&thread, nullptr, fd_adder, &actx), + SyscallSucceedsWithValue(0)); + + struct epoll_event result[kFDsPerEpoll]; + // Wait while no FDs are ready, but after 200ms fd_adder will add a ready FD + // to epoll which will wake us up. + EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(1)); + EXPECT_THAT(pthread_detach(thread), SyscallSucceeds()); + EXPECT_EQ(result[0].data.u64, 0xdeadbeeffacefeed); +} + +TEST(EpollTest, Oneshot) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + std::vector eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, + kMagicConstant + i)); + } + + struct epoll_event event; + event.events = EPOLLOUT | EPOLLONESHOT; + event.data.u64 = kMagicConstant; + ASSERT_THAT( + epoll_ctl(epollfd.get(), EPOLL_CTL_MOD, eventfds[0].get(), &event), + SyscallSucceeds()); + + struct epoll_event result[kFDsPerEpoll]; + // One-shot entry means that the first epoll_wait should succeed. + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(result[0].data.u64, kMagicConstant); + + // One-shot entry means that the second epoll_wait should timeout. + EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), + SyscallSucceedsWithValue(0)); +} + +TEST(EpollTest, EdgeTriggered_NoRandomSave) { + // Test edge-triggered entry: make it edge-triggered, first wait should + // return it, second one should time out, make it writable again, third wait + // should return it, fourth wait should timeout. + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfd.get(), + EPOLLOUT | EPOLLET, kMagicConstant)); + + struct epoll_event result[kFDsPerEpoll]; + + { + const DisableSave ds; // May trigger spurious event. + + // Edge-triggered entry means that the first epoll_wait should return the + // event. + ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(result[0].data.u64, kMagicConstant); + + // Edge-triggered entry means that the second epoll_wait should time out. + ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 100), + SyscallSucceedsWithValue(0)); + } + + uint64_t tmp = ULLONG_MAX - 1; + + // Make an fd non-writable. + ASSERT_THAT(WriteFd(eventfd.get(), &tmp, sizeof(tmp)), + SyscallSucceedsWithValue(sizeof(tmp))); + + // Make the same fd non-writable to trigger a change, which will trigger an + // edge-triggered event. + ASSERT_THAT(ReadFd(eventfd.get(), &tmp, sizeof(tmp)), + SyscallSucceedsWithValue(sizeof(tmp))); + + { + const DisableSave ds; // May trigger spurious event. + + // An edge-triggered event should now be returned. + ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(result[0].data.u64, kMagicConstant); + + // The edge-triggered event had been consumed above, we don't expect to + // get it again. + ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 100), + SyscallSucceedsWithValue(0)); + } +} + +TEST(EpollTest, OneshotAndEdgeTriggered) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfd.get(), + EPOLLOUT | EPOLLET | EPOLLONESHOT, + kMagicConstant)); + + struct epoll_event result[kFDsPerEpoll]; + // First time one shot edge-triggered entry means that epoll_wait should + // return the event. + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(result[0].data.u64, kMagicConstant); + + // Edge-triggered entry means that the second epoll_wait should time out. + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), + SyscallSucceedsWithValue(0)); + + uint64_t tmp = ULLONG_MAX - 1; + // Make an fd non-writable. + ASSERT_THAT(WriteFd(eventfd.get(), &tmp, sizeof(tmp)), + SyscallSucceedsWithValue(sizeof(tmp))); + // Make the same fd non-writable to trigger a change, which will not trigger + // an edge-triggered event because we've also included EPOLLONESHOT. + ASSERT_THAT(ReadFd(eventfd.get(), &tmp, sizeof(tmp)), + SyscallSucceedsWithValue(sizeof(tmp))); + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), + SyscallSucceedsWithValue(0)); +} + +TEST(EpollTest, CycleOfOneDisallowed) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + + struct epoll_event event; + event.events = EPOLLOUT; + event.data.u64 = kMagicConstant; + + ASSERT_THAT(epoll_ctl(epollfd.get(), EPOLL_CTL_ADD, epollfd.get(), &event), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(EpollTest, CycleOfThreeDisallowed) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + auto epollfd1 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + auto epollfd2 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + + ASSERT_NO_ERRNO( + RegisterEpollFD(epollfd.get(), epollfd1.get(), EPOLLIN, kMagicConstant)); + ASSERT_NO_ERRNO( + RegisterEpollFD(epollfd1.get(), epollfd2.get(), EPOLLIN, kMagicConstant)); + + struct epoll_event event; + event.events = EPOLLIN; + event.data.u64 = kMagicConstant; + EXPECT_THAT(epoll_ctl(epollfd2.get(), EPOLL_CTL_ADD, epollfd.get(), &event), + SyscallFailsWithErrno(ELOOP)); +} + +TEST(EpollTest, CloseFile) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); + ASSERT_NO_ERRNO( + RegisterEpollFD(epollfd.get(), eventfd.get(), EPOLLOUT, kMagicConstant)); + + struct epoll_event result[kFDsPerEpoll]; + ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(result[0].data.u64, kMagicConstant); + + // Close the event fd early. + eventfd.reset(); + + EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), + SyscallSucceedsWithValue(0)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc new file mode 100644 index 000000000..ffcd20622 --- /dev/null +++ b/test/syscalls/linux/eventfd.cc @@ -0,0 +1,189 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(EventfdTest, Nonblock) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE), + SyscallSucceeds()); + + uint64_t l; + ASSERT_THAT(read(efd, &l, sizeof(l)), SyscallFailsWithErrno(EAGAIN)); + + l = 1; + ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); + + l = 0; + ASSERT_THAT(read(efd, &l, sizeof(l)), SyscallSucceeds()); + EXPECT_EQ(l, 1); + + ASSERT_THAT(read(efd, &l, sizeof(l)), SyscallFailsWithErrno(EAGAIN)); +} + +void* read_three_times(void* arg) { + int efd = *reinterpret_cast(arg); + uint64_t l; + read(efd, &l, sizeof(l)); + read(efd, &l, sizeof(l)); + read(efd, &l, sizeof(l)); + return nullptr; +} + +TEST(EventfdTest, BlockingWrite) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_SEMAPHORE), SyscallSucceeds()); + + pthread_t p; + ASSERT_THAT(pthread_create(&p, nullptr, read_three_times, + reinterpret_cast(&efd)), + SyscallSucceeds()); + + uint64_t l = 1; + ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); + EXPECT_EQ(l, 1); + + ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); + EXPECT_EQ(l, 1); + + ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); + EXPECT_EQ(l, 1); + + ASSERT_THAT(pthread_join(p, nullptr), SyscallSucceeds()); +} + +TEST(EventfdTest, SmallWrite) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE), + SyscallSucceeds()); + + uint64_t l = 16; + ASSERT_THAT(write(efd, &l, 4), SyscallFailsWithErrno(EINVAL)); +} + +TEST(EventfdTest, SmallRead) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE), + SyscallSucceeds()); + + uint64_t l = 1; + ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); + + l = 0; + ASSERT_THAT(read(efd, &l, 4), SyscallFailsWithErrno(EINVAL)); +} + +TEST(EventfdTest, BigWrite) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE), + SyscallSucceeds()); + + uint64_t big[16]; + big[0] = 16; + ASSERT_THAT(write(efd, big, sizeof(big)), SyscallSucceeds()); +} + +TEST(EventfdTest, BigRead) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE), + SyscallSucceeds()); + + uint64_t l = 1; + ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); + + uint64_t big[16]; + ASSERT_THAT(read(efd, big, sizeof(big)), SyscallSucceeds()); + EXPECT_EQ(big[0], 1); +} + +TEST(EventfdTest, BigWriteBigRead) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE), + SyscallSucceeds()); + + uint64_t l[16]; + l[0] = 16; + ASSERT_THAT(write(efd, l, sizeof(l)), SyscallSucceeds()); + ASSERT_THAT(read(efd, l, sizeof(l)), SyscallSucceeds()); + EXPECT_EQ(l[0], 1); +} + +// NotifyNonZero is inherently racy, so random save is disabled. +TEST(EventfdTest, NotifyNonZero_NoRandomSave) { + // Waits will time out at 10 seconds. + constexpr int kEpollTimeoutMs = 10000; + // Create an eventfd descriptor. + int efd; + ASSERT_THAT(efd = eventfd(7, EFD_SEMAPHORE | EFD_NONBLOCK), + SyscallSucceeds()); + // Create an epoll fd to listen to efd. + int epollfd; + ASSERT_THAT(epollfd = epoll_create1(0), SyscallSucceeds()); + // Add efd to epoll. + struct epoll_event add_ev; + add_ev.events = EPOLLIN | EPOLLET; + add_ev.data.fd = efd; + ASSERT_THAT(epoll_ctl(epollfd, EPOLL_CTL_ADD, efd, &add_ev), + SyscallSucceeds()); + + // Use epoll to get a value from efd. + struct epoll_event out_ev; + int wait_out = epoll_wait(epollfd, &out_ev, 1, kEpollTimeoutMs); + EXPECT_EQ(wait_out, 1); + EXPECT_EQ(efd, out_ev.data.fd); + uint64_t val = 0; + ASSERT_THAT(read(efd, &val, sizeof(val)), SyscallSucceeds()); + EXPECT_EQ(val, 1); + + // Start a thread that, after this thread blocks on epoll_wait, will write to + // efd. This is racy -- it's possible that this write will happen after + // epoll_wait times out. + ScopedThread t([efd] { + sleep(5); + uint64_t val = 1; + write(efd, &val, sizeof(val)); + }); + + // epoll_wait should return once the thread writes. + wait_out = epoll_wait(epollfd, &out_ev, 1, kEpollTimeoutMs); + EXPECT_EQ(wait_out, 1); + EXPECT_EQ(efd, out_ev.data.fd); + + val = 0; + ASSERT_THAT(read(efd, &val, sizeof(val)), SyscallSucceeds()); + EXPECT_EQ(val, 1); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/exceptions.cc b/test/syscalls/linux/exceptions.cc new file mode 100644 index 000000000..72ab354e3 --- /dev/null +++ b/test/syscalls/linux/exceptions.cc @@ -0,0 +1,146 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "gtest/gtest.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +void inline Halt() { asm("hlt\r\n"); } + +void inline SetAlignmentCheck() { + asm("pushf\r\n" + "pop %%rax\r\n" + "or $0x40000, %%rax\r\n" + "push %%rax\r\n" + "popf\r\n" + : + : + : "ax"); +} + +void inline ClearAlignmentCheck() { + asm("pushf\r\n" + "pop %%rax\r\n" + "mov $0x40000, %%rbx\r\n" + "not %%rbx\r\n" + "and %%rbx, %%rax\r\n" + "push %%rax\r\n" + "popf\r\n" + : + : + : "ax", "bx"); +} + +void inline Int3Normal() { asm(".byte 0xcd, 0x03\r\n"); } + +void inline Int3Compact() { asm(".byte 0xcc\r\n"); } + +TEST(ExceptionTest, Halt) { + // In order to prevent the regular handler from messing with things (and + // perhaps refaulting until some other signal occurs), we reset the handler to + // the default action here and ensure that it dies correctly. + struct sigaction sa = {}; + sa.sa_handler = SIG_DFL; + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); + + EXPECT_EXIT(Halt(), ::testing::KilledBySignal(SIGSEGV), ""); +} + +TEST(ExceptionTest, DivideByZero) { + // See above. + struct sigaction sa = {}; + sa.sa_handler = SIG_DFL; + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); + + EXPECT_EXIT( + { + uint32_t remainder; + uint32_t quotient; + uint32_t divisor = 0; + uint64_t value = 1; + asm("divl 0(%2)\r\n" + : "=d"(remainder), "=a"(quotient) + : "r"(&divisor), "d"(value >> 32), "a"(value)); + TEST_CHECK(quotient > 0); // Force dependency. + }, + ::testing::KilledBySignal(SIGFPE), ""); +} + +TEST(ExceptionTest, Alignment) { + SetAlignmentCheck(); + ClearAlignmentCheck(); +} + +TEST(ExceptionTest, AlignmentHalt) { + // See above. + struct sigaction sa = {}; + sa.sa_handler = SIG_DFL; + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); + + // Reported upstream. We need to ensure that bad flags are cleared even in + // fault paths. Set the alignment flag and then generate an exception. + EXPECT_EXIT( + { + SetAlignmentCheck(); + Halt(); + }, + ::testing::KilledBySignal(SIGSEGV), ""); +} + +TEST(ExceptionTest, AlignmentCheck) { + + // See above. + struct sigaction sa = {}; + sa.sa_handler = SIG_DFL; + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGBUS, sa)); + + EXPECT_EXIT( + { + char array[16]; + SetAlignmentCheck(); + for (int i = 0; i < 8; i++) { + // At least 7/8 offsets will be unaligned here. + uint64_t* ptr = reinterpret_cast(&array[i]); + asm("mov %0, 0(%0)\r\n" : : "r"(ptr) : "ax"); + } + }, + ::testing::KilledBySignal(SIGBUS), ""); +} + +TEST(ExceptionTest, Int3Normal) { + // See above. + struct sigaction sa = {}; + sa.sa_handler = SIG_DFL; + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGTRAP, sa)); + + EXPECT_EXIT(Int3Normal(), ::testing::KilledBySignal(SIGTRAP), ""); +} + +TEST(ExceptionTest, Int3Compact) { + // See above. + struct sigaction sa = {}; + sa.sa_handler = SIG_DFL; + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGTRAP, sa)); + + EXPECT_EXIT(Int3Compact(), ::testing::KilledBySignal(SIGTRAP), ""); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/exec.cc b/test/syscalls/linux/exec.cc new file mode 100644 index 000000000..1ef40b502 --- /dev/null +++ b/test/syscalls/linux/exec.cc @@ -0,0 +1,625 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/exec.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr char kBasicWorkload[] = "exec_basic_workload"; +constexpr char kExitScript[] = "exit_script"; +constexpr char kStateWorkload[] = "exec_state_workload"; +constexpr char kProcExeWorkload[] = "exec_proc_exe_workload"; +constexpr char kAssertClosedWorkload[] = "exec_assert_closed_workload"; +constexpr char kPriorityWorkload[] = "priority_execve"; + +std::string WorkloadPath(absl::string_view binary) { + std::string full_path; + char* test_src = getenv("TEST_SRCDIR"); + if (test_src) { + full_path = JoinPath(test_src, "__main__/test/syscalls/linux", binary); + } + TEST_CHECK(full_path.empty() == false); + return full_path; +} + +constexpr char kExit42[] = "--exec_exit_42"; +constexpr char kExecWithThread[] = "--exec_exec_with_thread"; +constexpr char kExecFromThread[] = "--exec_exec_from_thread"; + +// Runs filename with argv and checks that the exit status is expect_status and +// that stderr contains expect_stderr. +void CheckOutput(const std::string& filename, const ExecveArray& argv, + const ExecveArray& envv, int expect_status, + const std::string& expect_stderr) { + int pipe_fds[2]; + ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds()); + + FileDescriptor read_fd(pipe_fds[0]); + FileDescriptor write_fd(pipe_fds[1]); + + pid_t child; + int execve_errno; + + const auto remap_stderr = [pipe_fds] { + // Remap stdin and stdout to /dev/null. + int fd = open("/dev/null", O_RDWR | O_CLOEXEC); + if (fd < 0) { + _exit(errno); + } + + int ret = dup2(fd, 0); + if (ret < 0) { + _exit(errno); + } + + ret = dup2(fd, 1); + if (ret < 0) { + _exit(errno); + } + + // And stderr to the pipe. + ret = dup2(pipe_fds[1], 2); + if (ret < 0) { + _exit(errno); + } + + // Here, we'd ideally close all other FDs inherited from the parent. + // However, that's not worth the effort and CloexecNormalFile and + // CloexecEventfd depend on that not happening. + }; + + auto kill = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(filename, argv, envv, remap_stderr, &child, &execve_errno)); + + ASSERT_EQ(0, execve_errno); + + // Not needed anymore. + write_fd.reset(); + + // Read stderr until the child exits. + std::string output; + constexpr int kSize = 128; + char buf[kSize]; + int n; + do { + ASSERT_THAT(n = ReadFd(read_fd.get(), buf, kSize), SyscallSucceeds()); + if (n > 0) { + output.append(buf, n); + } + } while (n > 0); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); + EXPECT_EQ(status, expect_status); + + // Process cleanup no longer needed. + kill.Release(); + + EXPECT_TRUE(absl::StrContains(output, expect_stderr)) << output; +} + +TEST(ExecDeathTest, EmptyPath) { + int execve_errno; + ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec("", {}, {}, nullptr, &execve_errno)); + EXPECT_EQ(execve_errno, ENOENT); +} + +TEST(ExecDeathTest, Basic) { + CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)}, {}, + ArgEnvExitStatus(0, 0), + absl::StrCat(WorkloadPath(kBasicWorkload), "\n")); +} + +TEST(ExecDeathTest, OneArg) { + CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload), "1"}, + {}, ArgEnvExitStatus(1, 0), + absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n")); +} + +TEST(ExecDeathTest, FiveArg) { + CheckOutput(WorkloadPath(kBasicWorkload), + {WorkloadPath(kBasicWorkload), "1", "2", "3", "4", "5"}, {}, + ArgEnvExitStatus(5, 0), + absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n2\n3\n4\n5\n")); +} + +TEST(ExecDeathTest, OneEnv) { + CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)}, + {"1"}, ArgEnvExitStatus(0, 1), + absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n")); +} + +TEST(ExecDeathTest, FiveEnv) { + CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)}, + {"1", "2", "3", "4", "5"}, ArgEnvExitStatus(0, 5), + absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n2\n3\n4\n5\n")); +} + +TEST(ExecDeathTest, OneArgOneEnv) { + CheckOutput(WorkloadPath(kBasicWorkload), + {WorkloadPath(kBasicWorkload), "arg"}, {"env"}, + ArgEnvExitStatus(1, 1), + absl::StrCat(WorkloadPath(kBasicWorkload), "\narg\nenv\n")); +} + +TEST(ExecDeathTest, InterpreterScript) { + CheckOutput(WorkloadPath(kExitScript), {WorkloadPath(kExitScript), "25"}, {}, + ArgEnvExitStatus(25, 0), ""); +} + +// Everything after the path in the interpreter script is a single argument. +TEST(ExecDeathTest, InterpreterScriptArgSplit) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kBasicWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo bar"), + 0755)); + + CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), + absl::StrCat(link.path(), "\nfoo bar\n", script.path(), "\n")); +} + +// Original argv[0] is replaced with the script path. +TEST(ExecDeathTest, InterpreterScriptArgvZero) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kBasicWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); + + CheckOutput(script.path(), {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), + absl::StrCat(link.path(), "\n", script.path(), "\n")); +} + +// Original argv[0] is replaced with the script path, exactly as passed to +// execve. +TEST(ExecDeathTest, InterpreterScriptArgvZeroRelative) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kBasicWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); + + auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); + auto script_relative = + ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); + + CheckOutput(script_relative, {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), + absl::StrCat(link.path(), "\n", script_relative, "\n")); +} + +// argv[0] is added as the script path, even if there was none. +TEST(ExecDeathTest, InterpreterScriptArgvZeroAdded) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kBasicWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); + + CheckOutput(script.path(), {}, {}, ArgEnvExitStatus(1, 0), + absl::StrCat(link.path(), "\n", script.path(), "\n")); +} + +// A NUL byte in the script line ends parsing. +TEST(ExecDeathTest, InterpreterScriptArgNUL) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kBasicWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), + absl::StrCat("#!", link.path(), " foo", std::string(1, '\0'), "bar"), 0755)); + + CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), + absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); +} + +// Trailing whitespace following interpreter path is ignored. +TEST(ExecDeathTest, InterpreterScriptTrailingWhitespace) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kBasicWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " "), 0755)); + + CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(1, 0), + absl::StrCat(link.path(), "\n", script.path(), "\n")); +} + +// Multiple whitespace characters between interpreter and arg allowed. +TEST(ExecDeathTest, InterpreterScriptArgWhitespace) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kBasicWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo"), 0755)); + + CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), + absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); +} + +TEST(ExecDeathTest, InterpreterScriptNoPath) { + TempPath script = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "#!", 0755)); + + int execve_errno; + ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, nullptr, &execve_errno)); + EXPECT_EQ(execve_errno, ENOEXEC); +} + +// AT_EXECFN is the path passed to execve. +TEST(ExecDeathTest, ExecFn) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kStateWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " PrintExecFn"), + 0755)); + + // Pass the script as a relative path and assert that is what appears in + // AT_EXECFN. + auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); + auto script_relative = + ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); + + CheckOutput(script_relative, {script_relative}, {}, ArgEnvExitStatus(0, 0), + absl::StrCat(script_relative, "\n")); +} + +TEST(ExecDeathTest, ExecName) { + std::string path = WorkloadPath(kStateWorkload); + + CheckOutput(path, {path, "PrintExecName"}, {}, ArgEnvExitStatus(0, 0), + absl::StrCat(Basename(path).substr(0, 15), "\n")); +} + +TEST(ExecDeathTest, ExecNameScript) { + // Symlink through /tmp to ensure the path is short enough. + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo("/tmp", WorkloadPath(kStateWorkload))); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), + absl::StrCat("#!", link.path(), " PrintExecName"), 0755)); + + std::string script_path = script.path(); + + CheckOutput(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), + absl::StrCat(Basename(script_path).substr(0, 15), "\n")); +} + +// execve may be called by a multithreaded process. +TEST(ExecDeathTest, WithSiblingThread) { + CheckOutput("/proc/self/exe", {"/proc/self/exe", kExecWithThread}, {}, + W_EXITCODE(42, 0), ""); +} + +// execve may be called from a thread other than the leader of a multithreaded +// process. +TEST(ExecDeathTest, FromSiblingThread) { + CheckOutput("/proc/self/exe", {"/proc/self/exe", kExecFromThread}, {}, + W_EXITCODE(42, 0), ""); +} + +TEST(ExecTest, NotFound) { + char* const argv[] = {nullptr}; + char* const envp[] = {nullptr}; + EXPECT_THAT(execve("/file/does/not/exist", argv, envp), + SyscallFailsWithErrno(ENOENT)); +} + +TEST(ExecTest, NoExecPerm) { + char* const argv[] = {nullptr}; + char* const envp[] = {nullptr}; + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + EXPECT_THAT(execve(f.path().c_str(), argv, envp), + SyscallFailsWithErrno(EACCES)); +} + +// A signal handler we never expect to be called. +void SignalHandler(int signo) { + std::cerr << "Signal " << signo << " raised." << std::endl; + exit(1); +} + +// Signal handlers are reset on execve(2), unless they have default or ignored +// disposition. +TEST(ExecStateDeathTest, HandlerReset) { + struct sigaction sa; + sa.sa_handler = SignalHandler; + ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); + + ExecveArray args = { + WorkloadPath(kStateWorkload), + "CheckSigHandler", + absl::StrCat(SIGUSR1), + absl::StrCat(absl::Hex(reinterpret_cast(SIG_DFL))), + }; + + CheckOutput(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); +} + +// Ignored signal dispositions are not reset. +TEST(ExecStateDeathTest, IgnorePreserved) { + struct sigaction sa; + sa.sa_handler = SIG_IGN; + ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); + + ExecveArray args = { + WorkloadPath(kStateWorkload), + "CheckSigHandler", + absl::StrCat(SIGUSR1), + absl::StrCat(absl::Hex(reinterpret_cast(SIG_IGN))), + }; + + CheckOutput(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); +} + +// Signal masks are not reset on exec +TEST(ExecStateDeathTest, SignalMask) { + sigset_t s; + sigemptyset(&s); + sigaddset(&s, SIGUSR1); + ASSERT_THAT(sigprocmask(SIG_BLOCK, &s, nullptr), SyscallSucceeds()); + + ExecveArray args = { + WorkloadPath(kStateWorkload), + "CheckSigBlocked", + absl::StrCat(SIGUSR1), + }; + + CheckOutput(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); +} + +// itimers persist across execve. +// N.B. Timers created with timer_create(2) should not be preserved! +TEST(ExecStateDeathTest, ItimerPreserved) { + // The fork in ForkAndExec clears itimers, so only set them up after fork. + auto setup_itimer = [] { + // Ignore SIGALRM, as we don't actually care about timer + // expirations. + struct sigaction sa; + sa.sa_handler = SIG_IGN; + int ret = sigaction(SIGALRM, &sa, nullptr); + if (ret < 0) { + _exit(errno); + } + + struct itimerval itv; + itv.it_interval.tv_sec = 1; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 1; + itv.it_value.tv_usec = 0; + ret = setitimer(ITIMER_REAL, &itv, nullptr); + if (ret < 0) { + _exit(errno); + } + }; + + std::string filename = WorkloadPath(kStateWorkload); + ExecveArray argv = { + filename, + "CheckItimerEnabled", + absl::StrCat(ITIMER_REAL), + }; + + pid_t child; + int execve_errno; + auto kill = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(filename, argv, {}, setup_itimer, &child, &execve_errno)); + ASSERT_EQ(0, execve_errno); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); + EXPECT_EQ(0, status); + + // Process cleanup no longer needed. + kill.Release(); +} + +TEST(ProcSelfExe, ChangesAcrossExecve) { + // See exec_proc_exe_workload for more details. We simply + // assert that the /proc/self/exe link changes across execve. + CheckOutput(WorkloadPath(kProcExeWorkload), + {WorkloadPath(kProcExeWorkload), + ASSERT_NO_ERRNO_AND_VALUE(ProcessExePath(getpid()))}, + {}, W_EXITCODE(0, 0), ""); +} + +TEST(ExecTest, CloexecNormalFile) { + const FileDescriptor fd_closed_on_exec = ASSERT_NO_ERRNO_AND_VALUE( + Open("/usr/share/zoneinfo", O_RDONLY | O_CLOEXEC)); + + CheckOutput(WorkloadPath(kAssertClosedWorkload), + {WorkloadPath(kAssertClosedWorkload), + absl::StrCat(fd_closed_on_exec.get())}, + {}, W_EXITCODE(0, 0), ""); + + // The assert closed workload exits with code 2 if the file still exists. We + // can use this to do a negative test. + const FileDescriptor fd_open_on_exec = + ASSERT_NO_ERRNO_AND_VALUE(Open("/usr/share/zoneinfo", O_RDONLY)); + + CheckOutput(WorkloadPath(kAssertClosedWorkload), + {WorkloadPath(kAssertClosedWorkload), + absl::StrCat(fd_open_on_exec.get())}, + {}, W_EXITCODE(2, 0), ""); +} + +TEST(ExecTest, CloexecEventfd) { + int efd; + ASSERT_THAT(efd = eventfd(0, EFD_CLOEXEC), SyscallSucceeds()); + FileDescriptor fd(efd); + + CheckOutput(WorkloadPath(kAssertClosedWorkload), + {WorkloadPath(kAssertClosedWorkload), absl::StrCat(fd.get())}, {}, + W_EXITCODE(0, 0), ""); +} + +// Priority consistent across calls to execve() +TEST(GetpriorityTest, ExecveMaintainsPriority) { + int prio = 16; + ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), prio), SyscallSucceeds()); + + // To avoid trying to use negative exit values, check for + // 20 - prio. Since prio should always be in the range [-20, 19], + // this leave expected_exit_code in the range [1, 40]. + int expected_exit_code = 20 - prio; + + // Program run (priority_execve) will exit(X) where + // X=getpriority(PRIO_PROCESS,0). Check that this exit value is prio. + CheckOutput(WorkloadPath(kPriorityWorkload), + {WorkloadPath(kPriorityWorkload)}, {}, + W_EXITCODE(expected_exit_code, 0), ""); +} + +void ExecWithThread() { + // Used to ensure that the thread has actually started. + absl::Mutex mu; + bool started = false; + + ScopedThread t([&] { + mu.Lock(); + started = true; + mu.Unlock(); + + while (true) { + pause(); + } + }); + + mu.LockWhen(absl::Condition(&started)); + mu.Unlock(); + + const ExecveArray argv = {"/proc/self/exe", kExit42}; + const ExecveArray envv; + + execve("/proc/self/exe", argv.get(), envv.get()); + exit(errno); +} + +void ExecFromThread() { + ScopedThread t([] { + const ExecveArray argv = {"/proc/self/exe", kExit42}; + const ExecveArray envv; + + execve("/proc/self/exe", argv.get(), envv.get()); + exit(errno); + }); + + while (true) { + pause(); + } +} + +bool ValidateProcCmdlineVsArgv(const int argc, const char* const* argv) { + auto contents_or = GetContents("/proc/self/cmdline"); + if (!contents_or.ok()) { + LOG(ERROR) << "Unable to get /proc/self/cmdline: " << contents_or.error(); + return false; + } + auto contents = contents_or.ValueOrDie(); + if (contents.back() != '\0') { + LOG(ERROR) << "Non-null terminated /proc/self/cmdline!"; + return false; + } + contents.pop_back(); + std::vector procfs_cmdline = absl::StrSplit(contents, '\0'); + + if (static_cast(procfs_cmdline.size()) != argc) { + LOG(ERROR) << "argc = " << argc << " != " << procfs_cmdline.size(); + return false; + } + + for (int i = 0; i < argc; ++i) { + if (procfs_cmdline[i] != argv[i]) { + LOG(ERROR) << "Procfs command line argument " << i << " mismatch " + << procfs_cmdline[i] << " != " << argv[i]; + return false; + } + } + return true; +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + // Start by validating that the stack argv is consistent with procfs. + if (!gvisor::testing::ValidateProcCmdlineVsArgv(argc, argv)) { + return 1; + } + + // Some of these tests require no background threads, so check for them before + // TestInit. + for (int i = 0; i < argc; i++) { + absl::string_view arg(argv[i]); + + if (arg == gvisor::testing::kExit42) { + return 42; + } + if (arg == gvisor::testing::kExecWithThread) { + gvisor::testing::ExecWithThread(); + return 1; + } + if (arg == gvisor::testing::kExecFromThread) { + gvisor::testing::ExecFromThread(); + return 1; + } + } + + gvisor::testing::TestInit(&argc, &argv); + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/exec.h b/test/syscalls/linux/exec.h new file mode 100644 index 000000000..b82bfffd1 --- /dev/null +++ b/test/syscalls/linux/exec.h @@ -0,0 +1,34 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_EXEC_H_ +#define GVISOR_TEST_SYSCALLS_EXEC_H_ + +#include + +namespace gvisor { +namespace testing { + +// Returns the exit code used by exec_basic_workload. +inline int ArgEnvExitCode(int args, int envs) { return args + envs * 10; } + +// Returns the exit status used by exec_basic_workload. +inline int ArgEnvExitStatus(int args, int envs) { + return W_EXITCODE(ArgEnvExitCode(args, envs), 0); +} + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_EXEC_H_ diff --git a/test/syscalls/linux/exec_assert_closed_workload.cc b/test/syscalls/linux/exec_assert_closed_workload.cc new file mode 100644 index 000000000..4448431e1 --- /dev/null +++ b/test/syscalls/linux/exec_assert_closed_workload.cc @@ -0,0 +1,45 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include + +#include "absl/strings/numbers.h" + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "need two arguments, got " << argc; + exit(1); + } + int fd; + if (!absl::SimpleAtoi(argv[1], &fd)) { + std::cerr << "fd: " << argv[1] << " could not be parsed" << std::endl; + exit(1); + } + struct stat s; + if (fstat(fd, &s) == 0) { + std::cerr << "fd: " << argv[1] << " should not be valid" << std::endl; + exit(2); + } + if (errno != EBADF) { + std::cerr << "fstat fd: " << argv[1] << " got errno: " << errno + << " wanted: " << EBADF << std::endl; + exit(1); + } + return 0; +} diff --git a/test/syscalls/linux/exec_basic_workload.cc b/test/syscalls/linux/exec_basic_workload.cc new file mode 100644 index 000000000..d4bdf511f --- /dev/null +++ b/test/syscalls/linux/exec_basic_workload.cc @@ -0,0 +1,31 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include + +#include "test/syscalls/linux/exec.h" + +int main(int argc, char** argv, char** envp) { + int i; + for (i = 0; i < argc; i++) { + std::cerr << argv[i] << std::endl; + } + for (i = 0; envp[i] != nullptr; i++) { + std::cerr << envp[i] << std::endl; + } + exit(gvisor::testing::ArgEnvExitCode(argc - 1, i)); + return 0; +} diff --git a/test/syscalls/linux/exec_binary.cc b/test/syscalls/linux/exec_binary.cc new file mode 100644 index 000000000..cfc898699 --- /dev/null +++ b/test/syscalls/linux/exec_binary.cc @@ -0,0 +1,1367 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/proc_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +using ::testing::AnyOf; +using ::testing::Eq; + +#ifndef __x86_64__ +// The assembly stub and ELF internal details must be ported to other arches. +#error "Test only supported on x86-64" +#endif // __x86_64__ + +// amd64 stub that calls PTRACE_TRACEME and sends itself SIGSTOP. +const char kPtraceCode[] = { + // movq $101, %rax /* ptrace */ + '\x48', + '\xc7', + '\xc0', + '\x65', + '\x00', + '\x00', + '\x00', + // movq $0, %rsi /* PTRACE_TRACEME */ + '\x48', + '\xc7', + '\xc6', + '\x00', + '\x00', + '\x00', + '\x00', + // movq $0, %rdi + '\x48', + '\xc7', + '\xc7', + '\x00', + '\x00', + '\x00', + '\x00', + // movq $0, %rdx + '\x48', + '\xc7', + '\xc2', + '\x00', + '\x00', + '\x00', + '\x00', + // movq $0, %r10 + '\x49', + '\xc7', + '\xc2', + '\x00', + '\x00', + '\x00', + '\x00', + // syscall + '\x0f', + '\x05', + + // movq $39, %rax /* getpid */ + '\x48', + '\xc7', + '\xc0', + '\x27', + '\x00', + '\x00', + '\x00', + // syscall + '\x0f', + '\x05', + + // movq %rax, %rdi /* pid */ + '\x48', + '\x89', + '\xc7', + // movq $62, %rax /* kill */ + '\x48', + '\xc7', + '\xc0', + '\x3e', + '\x00', + '\x00', + '\x00', + // movq $19, %rsi /* SIGSTOP */ + '\x48', + '\xc7', + '\xc6', + '\x13', + '\x00', + '\x00', + '\x00', + // syscall + '\x0f', + '\x05', +}; + +// Size of a syscall instruction. +constexpr int kSyscallSize = 2; + +// This test suite tests executable loading in the kernel (ELF and interpreter +// scripts). + +// Parameterized ELF types for 64 and 32 bit. +template +struct ElfTypes; + +template <> +struct ElfTypes<64> { + typedef Elf64_Ehdr ElfEhdr; + typedef Elf64_Phdr ElfPhdr; +}; + +template <> +struct ElfTypes<32> { + typedef Elf32_Ehdr ElfEhdr; + typedef Elf32_Phdr ElfPhdr; +}; + +template +struct ElfBinary { + using ElfEhdr = typename ElfTypes::ElfEhdr; + using ElfPhdr = typename ElfTypes::ElfPhdr; + + ElfEhdr header = {}; + std::vector phdrs; + std::vector data; + + // UpdateOffsets updates p_offset, p_vaddr in all phdrs to account for the + // space taken by the header and phdrs. + // + // It also updates header.e_phnum and adds the offset to header.e_entry to + // account for the headers residing in the first PT_LOAD segment. + // + // Before calling UpdateOffsets each of those fields should be the appropriate + // offset into data. + void UpdateOffsets() { + size_t offset = sizeof(header) + phdrs.size() * sizeof(ElfPhdr); + header.e_entry += offset; + header.e_phnum = phdrs.size(); + for (auto& p : phdrs) { + p.p_offset += offset; + p.p_vaddr += offset; + } + } + + // AddInterpreter adds a PT_INTERP segment with the passed contents. + // + // A later call to UpdateOffsets is required to make the new phdr valid. + void AddInterpreter(std::vector contents) { + const int start = data.size(); + data.insert(data.end(), contents.begin(), contents.end()); + const int size = data.size() - start; + + ElfPhdr phdr = {}; + phdr.p_type = PT_INTERP; + phdr.p_offset = start; + phdr.p_filesz = size; + phdr.p_memsz = size; + // "If [PT_INTERP] is present, it must precede any loadable segment entry." + phdrs.insert(phdrs.begin(), phdr); + } + + // Writes the header, phdrs, and data to fd. + PosixError Write(int fd) const { + int ret = WriteFd(fd, &header, sizeof(header)); + if (ret < 0) { + return PosixError(errno, "failed to write header"); + } else if (ret != sizeof(header)) { + return PosixError(EIO, absl::StrCat("short write of header: ", ret)); + } + + for (auto const& p : phdrs) { + ret = WriteFd(fd, &p, sizeof(p)); + if (ret < 0) { + return PosixError(errno, "failed to write phdr"); + } else if (ret != sizeof(p)) { + return PosixError(EIO, absl::StrCat("short write of phdr: ", ret)); + } + } + + ret = WriteFd(fd, data.data(), data.size()); + if (ret < 0) { + return PosixError(errno, "failed to write data"); + } else if (ret != static_cast(data.size())) { + return PosixError(EIO, absl::StrCat("short write of data: ", ret)); + } + + return NoError(); + } +}; + +// Creates a new temporary executable ELF file in parent with elf as the +// contents. +template +PosixErrorOr CreateElfWith(absl::string_view parent, + ElfBinary const& elf) { + ASSIGN_OR_RETURN_ERRNO( + auto file, TempPath::CreateFileWith(parent, absl::string_view(), 0755)); + ASSIGN_OR_RETURN_ERRNO(auto fd, Open(file.path(), O_RDWR)); + RETURN_IF_ERRNO(elf.Write(fd.get())); + return std::move(file); +} + +// Creates a new temporary executable ELF file with elf as the contents. +template +PosixErrorOr CreateElfWith(ElfBinary const& elf) { + return CreateElfWith(GetAbsoluteTestTmpdir(), elf); +} + +// Wait for pid to stop, and assert that it stopped via SIGSTOP. +PosixError WaitStopped(pid_t pid) { + int status; + int ret = RetryEINTR(waitpid)(pid, &status, 0); + MaybeSave(); + if (ret < 0) { + return PosixError(errno, "wait failed"); + } else if (ret != pid) { + return PosixError(ESRCH, absl::StrCat("wait got ", ret, " want ", pid)); + } + + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) { + return PosixError(EINVAL, + absl::StrCat("pid did not SIGSTOP; status = ", status)); + } + + return NoError(); +} + +// Returns a valid ELF that PTRACE_TRACEME and SIGSTOPs itself. +// +// UpdateOffsets must be called before writing this ELF. +ElfBinary<64> StandardElf() { + ElfBinary<64> elf; + elf.header.e_ident[EI_MAG0] = ELFMAG0; + elf.header.e_ident[EI_MAG1] = ELFMAG1; + elf.header.e_ident[EI_MAG2] = ELFMAG2; + elf.header.e_ident[EI_MAG3] = ELFMAG3; + elf.header.e_ident[EI_CLASS] = ELFCLASS64; + elf.header.e_ident[EI_DATA] = ELFDATA2LSB; + elf.header.e_ident[EI_VERSION] = EV_CURRENT; + elf.header.e_type = ET_EXEC; + elf.header.e_machine = EM_X86_64; + elf.header.e_version = EV_CURRENT; + elf.header.e_phoff = sizeof(elf.header); + elf.header.e_phentsize = sizeof(decltype(elf)::ElfPhdr); + + // TODO: Always include a PT_GNU_STACK segment to disable + // executable stacks. With this omitted the stack (and all PROT_READ) mappings + // should be executable, but gVisor doesn't support that. + decltype(elf)::ElfPhdr phdr = {}; + phdr.p_type = PT_GNU_STACK; + phdr.p_flags = PF_R | PF_W; + elf.phdrs.push_back(phdr); + + phdr = {}; + phdr.p_type = PT_LOAD; + phdr.p_flags = PF_R | PF_X; + phdr.p_offset = 0; + phdr.p_vaddr = 0x40000; + phdr.p_filesz = sizeof(kPtraceCode); + phdr.p_memsz = phdr.p_filesz; + elf.phdrs.push_back(phdr); + + elf.header.e_entry = phdr.p_vaddr; + + elf.data.assign(kPtraceCode, kPtraceCode + sizeof(kPtraceCode)); + + return elf; +} + +// Test that a trivial binary executes. +TEST(ElfTest, Execute) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + // Ensure it made it to SIGSTOP. + ASSERT_NO_ERRNO(WaitStopped(child)); + + struct user_regs_struct regs; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child, 0, ®s), SyscallSucceeds()); + // RIP is just beyond the final syscall instruction. + EXPECT_EQ(regs.rip, elf.header.e_entry + sizeof(kPtraceCode)); + + EXPECT_THAT(child, ContainsMappings(std::vector({ + {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, + file.path().c_str()}, + }))); +} + +// StandardElf without data completes execve, but faults once running. +TEST(ElfTest, MissingText) { + ElfBinary<64> elf = StandardElf(); + elf.data.clear(); + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + // It runs off the end of the zeroes filling the end of the page. + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) << status; +} + +// Typical ELF with a data + bss segment +TEST(ElfTest, DataSegment) { + ElfBinary<64> elf = StandardElf(); + + // Create a standard ELF, but extend to 1.5 pages. The second page will be the + // beginning of a multi-page data + bss segment. + elf.data.resize(kPageSize + kPageSize / 2); + + decltype(elf)::ElfPhdr phdr = {}; + phdr.p_type = PT_LOAD; + phdr.p_flags = PF_R | PF_W; + phdr.p_offset = kPageSize; + phdr.p_vaddr = 0x41000; + phdr.p_filesz = kPageSize / 2; + // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a + // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. + phdr.p_memsz = 2 * kPageSize - kPageSize / 2; + elf.phdrs.push_back(phdr); + + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + EXPECT_THAT( + child, ContainsMappings(std::vector({ + // text page. + {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, + file.path().c_str()}, + // data + bss page from file. + {0x41000, 0x42000, true, true, false, true, kPageSize, 0, 0, 0, + file.path().c_str()}, + // bss page from anon. + {0x42000, 0x43000, true, true, false, true, 0, 0, 0, 0, ""}, + }))); +} + +// Linux will allow PT_LOAD segments to overlap. +TEST(ElfTest, DirectlyOverlappingSegments) { + // NOTE: see PIEOutOfOrderSegments. + SKIP_IF(IsRunningOnGvisor()); + + ElfBinary<64> elf = StandardElf(); + + // Same as the StandardElf mapping. + decltype(elf)::ElfPhdr phdr = {}; + phdr.p_type = PT_LOAD; + // Add PF_W so we can differentiate this mapping from the first. + phdr.p_flags = PF_R | PF_W | PF_X; + phdr.p_offset = 0; + phdr.p_vaddr = 0x40000; + phdr.p_filesz = sizeof(kPtraceCode); + phdr.p_memsz = phdr.p_filesz; + elf.phdrs.push_back(phdr); + + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + EXPECT_THAT(child, ContainsMappings(std::vector({ + {0x40000, 0x41000, true, true, true, true, 0, 0, 0, 0, + file.path().c_str()}, + }))); +} + +// Linux allows out-of-order PT_LOAD segments. +TEST(ElfTest, OutOfOrderSegments) { + // NOTE: see PIEOutOfOrderSegments. + SKIP_IF(IsRunningOnGvisor()); + + ElfBinary<64> elf = StandardElf(); + + decltype(elf)::ElfPhdr phdr = {}; + phdr.p_type = PT_LOAD; + phdr.p_flags = PF_R | PF_X; + phdr.p_offset = 0; + phdr.p_vaddr = 0x20000; + phdr.p_filesz = sizeof(kPtraceCode); + phdr.p_memsz = phdr.p_filesz; + elf.phdrs.push_back(phdr); + + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + EXPECT_THAT(child, ContainsMappings(std::vector({ + {0x20000, 0x21000, true, false, true, true, 0, 0, 0, 0, + file.path().c_str()}, + {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, + file.path().c_str()}, + }))); +} + +// header.e_phoff is bound the end of the file. +TEST(ElfTest, OutOfBoundsPhdrs) { + ElfBinary<64> elf = StandardElf(); + elf.header.e_phoff = 0x100000; + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + // On Linux 3.11, this caused EIO. On newer Linux, it causes ENOEXEC. + EXPECT_THAT(execve_errno, AnyOf(Eq(ENOEXEC), Eq(EIO))); +} + +// Claim there is a phdr beyond the end of the file, but don't include it. +TEST(ElfTest, MissingPhdr) { + ElfBinary<64> elf = StandardElf(); + + // Clear data so the file ends immediately after the phdrs. + // N.B. Per ElfTest.MissingData, StandardElf without data completes execve + // without error. + elf.data.clear(); + elf.UpdateOffsets(); + + // Claim that there is another phdr just beyond the end of the file. Of + // course, it isn't accessible. + elf.header.e_phnum++; + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + // On Linux 3.11, this caused EIO. On newer Linux, it causes ENOEXEC. + EXPECT_THAT(execve_errno, AnyOf(Eq(ENOEXEC), Eq(EIO))); +} + +// No headers at all, just the ELF magic. +TEST(ElfTest, MissingHeader) { + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0755)); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); + + const char kElfMagic[] = {0x7f, 'E', 'L', 'F'}; + + ASSERT_THAT(WriteFd(fd.get(), &kElfMagic, sizeof(kElfMagic)), + SyscallSucceeds()); + fd.reset(); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + EXPECT_EQ(execve_errno, ENOEXEC); +} + +// Load a PIE ELF with a data + bss segment. +TEST(ElfTest, PIE) { + ElfBinary<64> elf = StandardElf(); + + elf.header.e_type = ET_DYN; + + // Create a standard ELF, but extend to 1.5 pages. The second page will be the + // beginning of a multi-page data + bss segment. + elf.data.resize(kPageSize + kPageSize / 2); + + elf.header.e_entry = 0x0; + + decltype(elf)::ElfPhdr phdr = {}; + phdr.p_type = PT_LOAD; + phdr.p_flags = PF_R | PF_W; + phdr.p_offset = kPageSize; + // Put the data segment at a bit of an offset. + phdr.p_vaddr = 0x20000; + phdr.p_filesz = kPageSize / 2; + // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a + // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. + phdr.p_memsz = 2 * kPageSize - kPageSize / 2; + elf.phdrs.push_back(phdr); + + elf.UpdateOffsets(); + + // The first segment really needs to start at 0 for a normal PIE binary, and + // thus includes the headers. + const uint64_t offset = elf.phdrs[1].p_offset; + elf.phdrs[1].p_offset = 0x0; + elf.phdrs[1].p_vaddr = 0x0; + elf.phdrs[1].p_filesz += offset; + elf.phdrs[1].p_memsz += offset; + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + // RIP tells us which page the first segment was loaded into. + struct user_regs_struct regs; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child, 0, ®s), SyscallSucceeds()); + + const uint64_t load_addr = regs.rip & ~(kPageSize - 1); + + EXPECT_THAT(child, ContainsMappings(std::vector({ + // text page. + {load_addr, load_addr + 0x1000, true, false, true, + true, 0, 0, 0, 0, file.path().c_str()}, + // data + bss page from file. + {load_addr + 0x20000, load_addr + 0x21000, true, true, + false, true, kPageSize, 0, 0, 0, file.path().c_str()}, + // bss page from anon. + {load_addr + 0x21000, load_addr + 0x22000, true, true, + false, true, 0, 0, 0, 0, ""}, + }))); +} + +// PIE binary with a non-zero start address. +// +// This is non-standard for a PIE binary, but valid. The binary is still loaded +// at an arbitrary address, not the first PT_LOAD vaddr. +// +// N.B. Linux changed this behavior in d1fd836dcf00d2028c700c7e44d2c23404062c90. +// Previously, with "randomization" enabled, PIE binaries with a non-zero start +// address would be be loaded at the address they specified because mmap was +// passed the load address, which wasn't 0 as expected. +// +// This change is present in kernel v4.1+. +TEST(ElfTest, PIENonZeroStart) { + // gVisor has the newer behavior. + if (!IsRunningOnGvisor()) { + auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); + SKIP_IF(version.major < 4 || (version.major == 4 && version.minor < 1)); + } + + ElfBinary<64> elf = StandardElf(); + + elf.header.e_type = ET_DYN; + + // Create a standard ELF, but extend to 1.5 pages. The second page will be the + // beginning of a multi-page data + bss segment. + elf.data.resize(kPageSize + kPageSize / 2); + + decltype(elf)::ElfPhdr phdr = {}; + phdr.p_type = PT_LOAD; + phdr.p_flags = PF_R | PF_W; + phdr.p_offset = kPageSize; + // Put the data segment at a bit of an offset. + phdr.p_vaddr = 0x60000; + phdr.p_filesz = kPageSize / 2; + // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a + // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. + phdr.p_memsz = 2 * kPageSize - kPageSize / 2; + elf.phdrs.push_back(phdr); + + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + // RIP tells us which page the first segment was loaded into. + struct user_regs_struct regs; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child, 0, ®s), SyscallSucceeds()); + + const uint64_t load_addr = regs.rip & ~(kPageSize - 1); + + // The ELF is loaded at an arbitrary address, not the first PT_LOAD vaddr. + // + // N.B. this is technically flaky, but Linux is *extremely* unlikely to pick + // this as the start address, as it searches from the top down. + EXPECT_NE(load_addr, 0x40000); + + EXPECT_THAT(child, ContainsMappings(std::vector({ + // text page. + {load_addr, load_addr + 0x1000, true, false, true, + true, 0, 0, 0, 0, file.path().c_str()}, + // data + bss page from file. + {load_addr + 0x20000, load_addr + 0x21000, true, true, + false, true, kPageSize, 0, 0, 0, file.path().c_str()}, + // bss page from anon. + {load_addr + 0x21000, load_addr + 0x22000, true, true, + false, true, 0, 0, 0, 0, ""}, + }))); +} + +TEST(ElfTest, PIEOutOfOrderSegments) { + // TODO: This triggers a bug in Linux where it computes the size + // of the binary as 0x20000 - 0x40000 = 0xfffffffffffe0000, which obviously + // fails to map. + // + // We test gVisor's behavior (of rejecting the binary) because I assert that + // Linux is wrong and needs to be fixed. + SKIP_IF(!IsRunningOnGvisor()); + + ElfBinary<64> elf = StandardElf(); + + elf.header.e_type = ET_DYN; + + // Create a standard ELF, but extend to 1.5 pages. The second page will be the + // beginning of a multi-page data + bss segment. + elf.data.resize(kPageSize + kPageSize / 2); + + decltype(elf)::ElfPhdr phdr = {}; + phdr.p_type = PT_LOAD; + phdr.p_flags = PF_R | PF_W; + phdr.p_offset = kPageSize; + // Put the data segment *before* the first segment. + phdr.p_vaddr = 0x20000; + phdr.p_filesz = kPageSize / 2; + // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a + // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. + phdr.p_memsz = 2 * kPageSize - kPageSize / 2; + elf.phdrs.push_back(phdr); + + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + EXPECT_EQ(execve_errno, ENOEXEC); +} + +// Standard dynamically linked binary with an ELF interpreter. +TEST(ElfTest, ELFInterpreter) { + ElfBinary<64> interpreter = StandardElf(); + interpreter.header.e_type = ET_DYN; + interpreter.header.e_entry = 0x0; + interpreter.UpdateOffsets(); + + // The first segment really needs to start at 0 for a normal PIE binary, and + // thus includes the headers. + uint64_t const offset = interpreter.phdrs[1].p_offset; + interpreter.phdrs[1].p_offset = 0x0; + interpreter.phdrs[1].p_vaddr = 0x0; + interpreter.phdrs[1].p_filesz += offset; + interpreter.phdrs[1].p_memsz += offset; + + TempPath interpreter_file = + ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); + + ElfBinary<64> binary = StandardElf(); + + // Append the interpreter path. + int const interp_data_start = binary.data.size(); + for (char const c : interpreter_file.path()) { + binary.data.push_back(c); + } + // NUL-terminate. + binary.data.push_back(0); + int const interp_data_size = binary.data.size() - interp_data_start; + + decltype(binary)::ElfPhdr phdr = {}; + phdr.p_type = PT_INTERP; + phdr.p_offset = interp_data_start; + phdr.p_filesz = interp_data_size; + phdr.p_memsz = interp_data_size; + // "If [PT_INTERP] is present, it must precede any loadable segment entry." + // + // However, Linux allows it anywhere, so we just stick it at the end to make + // sure out-of-order PT_INTERP is OK. + binary.phdrs.push_back(phdr); + + binary.UpdateOffsets(); + + TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( + binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + // RIP tells us which page the first segment of the interpreter was loaded + // into. + struct user_regs_struct regs; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child, 0, ®s), SyscallSucceeds()); + + const uint64_t interp_load_addr = regs.rip & ~(kPageSize - 1); + + EXPECT_THAT(child, + ContainsMappings(std::vector({ + // Main binary + {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, + binary_file.path().c_str()}, + // Interpreter + {interp_load_addr, interp_load_addr + 0x1000, true, false, + true, true, 0, 0, 0, 0, interpreter_file.path().c_str()}, + }))); +} + +// Test parameter to ElfInterpterStaticTest cases. The first item is a suffix to +// add to the end of the interpreter path in the PT_INTERP segment and the +// second is the expected execve(2) errno. +using ElfInterpreterStaticParam = std::tuple, int>; + +class ElfInterpreterStaticTest + : public ::testing::TestWithParam {}; + +// Statically linked ELF with a statically linked ELF interpreter. +TEST_P(ElfInterpreterStaticTest, Test) { + const std::vector segment_suffix = std::get<0>(GetParam()); + const int expected_errno = std::get<1>(GetParam()); + + ElfBinary<64> interpreter = StandardElf(); + interpreter.UpdateOffsets(); + TempPath interpreter_file = + ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); + + ElfBinary<64> binary = StandardElf(); + // The PT_LOAD segment conflicts with the interpreter's PT_LOAD segment. The + // interpreter's will be mapped directly over the binary's. + + // Interpreter path plus the parameterized suffix in the PT_INTERP segment. + const std::string path = interpreter_file.path(); + std::vector segment(path.begin(), path.end()); + segment.insert(segment.end(), segment_suffix.begin(), segment_suffix.end()); + binary.AddInterpreter(segment); + + binary.UpdateOffsets(); + + TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( + binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, expected_errno); + + if (expected_errno == 0) { + ASSERT_NO_ERRNO(WaitStopped(child)); + + EXPECT_THAT(child, ContainsMappings(std::vector({ + // Interpreter. + {0x40000, 0x41000, true, false, true, true, 0, 0, 0, + 0, interpreter_file.path().c_str()}, + }))); + } +} + +INSTANTIATE_TEST_CASE_P( + Cases, ElfInterpreterStaticTest, + ::testing::ValuesIn({ + // Simple NUL-terminator to run the interpreter as normal. + std::make_tuple(std::vector({'\0'}), 0), + // Add some garbage to the segment followed by a NUL-terminator. This is + // ignored. + std::make_tuple(std::vector({'\0', 'b', '\0'}), 0), + // Add some garbage to the segment without a NUL-terminator. Linux will + // reject + // this. + std::make_tuple(std::vector({'\0', 'b'}), ENOEXEC), + })); + +// Test parameter to ElfInterpterBadPathTest cases. The first item is the +// contents of the PT_INTERP segment and the second is the expected execve(2) +// errno. +using ElfInterpreterBadPathParam = std::tuple, int>; + +class ElfInterpreterBadPathTest + : public ::testing::TestWithParam {}; + +TEST_P(ElfInterpreterBadPathTest, Test) { + const std::vector segment = std::get<0>(GetParam()); + const int expected_errno = std::get<1>(GetParam()); + + ElfBinary<64> binary = StandardElf(); + binary.AddInterpreter(segment); + binary.UpdateOffsets(); + + TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); + + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( + binary_file.path(), {binary_file.path()}, {}, nullptr, &execve_errno)); + EXPECT_EQ(execve_errno, expected_errno); +} + +INSTANTIATE_TEST_CASE_P( + Cases, ElfInterpreterBadPathTest, + ::testing::ValuesIn({ + // NUL-terminated fake path in the PT_INTERP segment. + std::make_tuple(std::vector({'/', 'f', '/', 'b', '\0'}), ENOENT), + // ELF interpreter not NUL-terminated. + std::make_tuple(std::vector({'/', 'f', '/', 'b'}), ENOEXEC), + // ELF interpreter path omitted entirely. + // + // fs/binfmt_elf.c:load_elf_binary returns ENOEXEC if p_filesz is < 2 + // bytes. + std::make_tuple(std::vector({'\0'}), ENOEXEC), + // ELF interpreter path = "\0". + // + // fs/binfmt_elf.c:load_elf_binary returns ENOEXEC if p_filesz is < 2 + // bytes, so add an extra byte to pass that check. + // + // load_elf_binary -> open_exec -> do_open_execat fails to check that + // name != '\0' before calling do_filp_open, which thus opens the + // working directory. do_open_execat returns EACCES because the + // directory is not a regular file. + std::make_tuple(std::vector({'\0', '\0'}), EACCES), + })); + +// Relative path to ELF interpreter. +TEST(ElfTest, ELFInterpreterRelative) { + ElfBinary<64> interpreter = StandardElf(); + interpreter.header.e_type = ET_DYN; + interpreter.header.e_entry = 0x0; + interpreter.UpdateOffsets(); + + // The first segment really needs to start at 0 for a normal PIE binary, and + // thus includes the headers. + uint64_t const offset = interpreter.phdrs[1].p_offset; + interpreter.phdrs[1].p_offset = 0x0; + interpreter.phdrs[1].p_vaddr = 0x0; + interpreter.phdrs[1].p_filesz += offset; + interpreter.phdrs[1].p_memsz += offset; + + TempPath interpreter_file = + ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); + auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); + auto interpreter_relative = + ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, interpreter_file.path())); + + ElfBinary<64> binary = StandardElf(); + + // NUL-terminated path in the PT_INTERP segment. + std::vector segment(interpreter_relative.begin(), + interpreter_relative.end()); + segment.push_back(0); + binary.AddInterpreter(segment); + + binary.UpdateOffsets(); + + TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( + binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + // RIP tells us which page the first segment of the interpreter was loaded + // into. + struct user_regs_struct regs; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child, 0, ®s), SyscallSucceeds()); + + const uint64_t interp_load_addr = regs.rip & ~(kPageSize - 1); + + EXPECT_THAT(child, + ContainsMappings(std::vector({ + // Main binary + {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, + binary_file.path().c_str()}, + // Interpreter + {interp_load_addr, interp_load_addr + 0x1000, true, false, + true, true, 0, 0, 0, 0, interpreter_file.path().c_str()}, + }))); +} + +// ELF interpreter architecture doesn't match the binary. +TEST(ElfTest, ELFInterpreterWrongArch) { + ElfBinary<64> interpreter = StandardElf(); + interpreter.header.e_machine = EM_PPC64; + interpreter.header.e_type = ET_DYN; + interpreter.header.e_entry = 0x0; + interpreter.UpdateOffsets(); + + // The first segment really needs to start at 0 for a normal PIE binary, and + // thus includes the headers. + uint64_t const offset = interpreter.phdrs[1].p_offset; + interpreter.phdrs[1].p_offset = 0x0; + interpreter.phdrs[1].p_vaddr = 0x0; + interpreter.phdrs[1].p_filesz += offset; + interpreter.phdrs[1].p_memsz += offset; + + TempPath interpreter_file = + ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); + + ElfBinary<64> binary = StandardElf(); + + // NUL-terminated path in the PT_INTERP segment. + const std::string path = interpreter_file.path(); + std::vector segment(path.begin(), path.end()); + segment.push_back(0); + binary.AddInterpreter(segment); + + binary.UpdateOffsets(); + + TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( + binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, ELIBBAD); +} + +// No execute permissions on the binary. +TEST(ElfTest, NoExecute) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + ASSERT_THAT(chmod(file.path().c_str(), 0644), SyscallSucceeds()); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + EXPECT_EQ(execve_errno, EACCES); +} + +// Execute, but no read permissions on the binary works just fine. +TEST(ElfTest, NoRead) { + // TODO: gVisor's backing filesystem may prevent the sentry from + // reading the executable. + SKIP_IF(IsRunningOnGvisor()); + + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + ASSERT_THAT(chmod(file.path().c_str(), 0111), SyscallSucceeds()); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + ASSERT_NO_ERRNO(WaitStopped(child)); + + // TODO: A task with a non-readable executable is marked + // non-dumpable, preventing access to proc files. gVisor does not implement + // this behavior. +} + +// No execute permissions on the ELF interpreter. +TEST(ElfTest, ElfInterpreterNoExecute) { + ElfBinary<64> interpreter = StandardElf(); + interpreter.header.e_type = ET_DYN; + interpreter.header.e_entry = 0x0; + interpreter.UpdateOffsets(); + + // The first segment really needs to start at 0 for a normal PIE binary, and + // thus includes the headers. + uint64_t const offset = interpreter.phdrs[1].p_offset; + interpreter.phdrs[1].p_offset = 0x0; + interpreter.phdrs[1].p_vaddr = 0x0; + interpreter.phdrs[1].p_filesz += offset; + interpreter.phdrs[1].p_memsz += offset; + + TempPath interpreter_file = + ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); + + ElfBinary<64> binary = StandardElf(); + + // NUL-terminated path in the PT_INTERP segment. + const std::string path = interpreter_file.path(); + std::vector segment(path.begin(), path.end()); + segment.push_back(0); + binary.AddInterpreter(segment); + + binary.UpdateOffsets(); + + TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); + + ASSERT_THAT(chmod(interpreter_file.path().c_str(), 0644), SyscallSucceeds()); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(interpreter_file.path(), {interpreter_file.path()}, {}, + &child, &execve_errno)); + EXPECT_EQ(execve_errno, EACCES); +} + +// Execute a basic interpreter script. +TEST(InterpreterScriptTest, Execute) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + // Use /tmp explicitly to ensure the path is short enough. + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0755)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + EXPECT_NO_ERRNO(WaitStopped(child)); +} + +// Whitespace after #!. +TEST(InterpreterScriptTest, Whitespace) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + // Use /tmp explicitly to ensure the path is short enough. + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#! \t \t", binary.path()), 0755)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + EXPECT_NO_ERRNO(WaitStopped(child)); +} + +// Interpreter script is missing execute permission. +TEST(InterpreterScriptTest, InterpreterScriptNoExecute) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + // Use /tmp explicitly to ensure the path is short enough. + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0644)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, EACCES); +} + +// Binary interpreter script refers to is missing execute permission. +TEST(InterpreterScriptTest, BinaryNoExecute) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + // Use /tmp explicitly to ensure the path is short enough. + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + ASSERT_THAT(chmod(binary.path().c_str(), 0644), SyscallSucceeds()); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0755)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, EACCES); +} + +// Linux will load interpreter scripts five levels deep, but no more. +TEST(InterpreterScriptTest, MaxRecursion) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + // Use /tmp explicitly to ensure the path is short enough. + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + TempPath script1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + "/tmp", absl::StrCat("#!", binary.path()), 0755)); + TempPath script2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + "/tmp", absl::StrCat("#!", script1.path()), 0755)); + TempPath script3 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + "/tmp", absl::StrCat("#!", script2.path()), 0755)); + TempPath script4 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + "/tmp", absl::StrCat("#!", script3.path()), 0755)); + TempPath script5 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + "/tmp", absl::StrCat("#!", script4.path()), 0755)); + TempPath script6 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + "/tmp", absl::StrCat("#!", script5.path()), 0755)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script6.path(), {script6.path()}, {}, &child, &execve_errno)); + // Too many levels of recursion. + EXPECT_EQ(execve_errno, ELOOP); + + // The next level up is OK. + auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script5.path(), {script5.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + EXPECT_NO_ERRNO(WaitStopped(child)); +} + +// Interpreter script with a relative path. +TEST(InterpreterScriptTest, RelativePath) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); + auto binary_relative = + ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, binary.path())); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary_relative), 0755)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + EXPECT_NO_ERRNO(WaitStopped(child)); +} + +// Interpreter script with .. in a path component. +TEST(InterpreterScriptTest, UncleanPath) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + // Use /tmp explicitly to ensure the path is short enough. + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!/tmp/../", binary.path()), + 0755)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + EXPECT_NO_ERRNO(WaitStopped(child)); +} + +// Passed interpreter script is a symlink. +TEST(InterpreterScriptTest, Symlink) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + // Use /tmp explicitly to ensure the path is short enough. + TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0755)); + + TempPath link = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), script.path())); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(link.path(), {link.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + EXPECT_NO_ERRNO(WaitStopped(child)); +} + +// Interpreter script points to a symlink loop. +TEST(InterpreterScriptTest, SymlinkLoop) { + std::string const link1 = NewTempAbsPathInDir("/tmp"); + std::string const link2 = NewTempAbsPathInDir("/tmp"); + + ASSERT_THAT(symlink(link2.c_str(), link1.c_str()), SyscallSucceeds()); + auto remove_link1 = Cleanup( + [&link1] { EXPECT_THAT(unlink(link1.c_str()), SyscallSucceeds()); }); + + ASSERT_THAT(symlink(link1.c_str(), link2.c_str()), SyscallSucceeds()); + auto remove_link2 = Cleanup( + [&link2] { EXPECT_THAT(unlink(link2.c_str()), SyscallSucceeds()); }); + + TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::StrCat("#!", link1), 0755)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); + EXPECT_EQ(execve_errno, ELOOP); +} + +// Binary is a symlink loop. +TEST(ExecveTest, SymlinkLoop) { + std::string const link1 = NewTempAbsPathInDir("/tmp"); + std::string const link2 = NewTempAbsPathInDir("/tmp"); + + ASSERT_THAT(symlink(link2.c_str(), link1.c_str()), SyscallSucceeds()); + auto remove_link = Cleanup( + [&link1] { EXPECT_THAT(unlink(link1.c_str()), SyscallSucceeds()); }); + + ASSERT_THAT(symlink(link1.c_str(), link2.c_str()), SyscallSucceeds()); + auto remove_link2 = Cleanup( + [&link2] { EXPECT_THAT(unlink(link2.c_str()), SyscallSucceeds()); }); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(link1, {link1}, {}, &child, &execve_errno)); + EXPECT_EQ(execve_errno, ELOOP); +} + +// Binary is a directory. +TEST(ExecveTest, Directory) { + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec("/tmp", {"/tmp"}, {}, &child, &execve_errno)); + EXPECT_EQ(execve_errno, EACCES); +} + +// Pass a valid binary as a directory (extra / on the end). +TEST(ExecveTest, BinaryAsDirectory) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + std::string const path = absl::StrCat(file.path(), "/"); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(path, {path}, {}, &child, &execve_errno)); + EXPECT_EQ(execve_errno, ENOTDIR); +} + +// The initial brk value is after the page at the end of the binary. +TEST(ExecveTest, BrkAfterBinary) { + ElfBinary<64> elf = StandardElf(); + elf.UpdateOffsets(); + + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); + + pid_t child; + int execve_errno; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); + ASSERT_EQ(execve_errno, 0); + + // Ensure it made it to SIGSTOP. + ASSERT_NO_ERRNO(WaitStopped(child)); + + struct user_regs_struct regs; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child, 0, ®s), SyscallSucceeds()); + + // RIP is just beyond the final syscall instruction. Rewind to execute a brk + // syscall. + regs.rip -= kSyscallSize; + regs.rax = __NR_brk; + regs.rdi = 0; + ASSERT_THAT(ptrace(PTRACE_SETREGS, child, 0, ®s), SyscallSucceeds()); + + // Resume the child, waiting for syscall entry. + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child, 0, 0), SyscallSucceeds()); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) + << "status = " << status; + + // Execute the syscall. + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child, 0, 0), SyscallSucceeds()); + ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) + << "status = " << status; + + ASSERT_THAT(ptrace(PTRACE_GETREGS, child, 0, ®s), SyscallSucceeds()); + + // brk is after the text page. + // + // The kernel does brk randomization, so we can't be sure what the exact + // address will be, but it is always beyond the final page in the binary. + // i.e., it does not start immediately after memsz in the middle of a page. + // Userspace may expect to use that space. + EXPECT_GE(regs.rax, 0x41000); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/exec_proc_exe_workload.cc b/test/syscalls/linux/exec_proc_exe_workload.cc new file mode 100644 index 000000000..b9a4ac749 --- /dev/null +++ b/test/syscalls/linux/exec_proc_exe_workload.cc @@ -0,0 +1,35 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include + +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" + +int main(int argc, char** argv, char** envp) { + std::string exe = gvisor::testing::ProcessExePath(getpid()).ValueOrDie(); + if (exe[0] != '/') { + std::cerr << "relative path: " << exe << std::endl; + exit(1); + } + if (exe.find(argv[1]) != std::string::npos) { + std::cerr << "matching path: " << exe << std::endl; + exit(1); + } + + return 0; +} diff --git a/test/syscalls/linux/exec_state_workload.cc b/test/syscalls/linux/exec_state_workload.cc new file mode 100644 index 000000000..b66e22565 --- /dev/null +++ b/test/syscalls/linux/exec_state_workload.cc @@ -0,0 +1,202 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Pretty-print a sigset_t. +std::ostream& operator<<(std::ostream& out, const sigset_t& s) { + out << "{ "; + + for (int i = 0; i < NSIG; i++) { + if (sigismember(&s, i)) { + out << i << " "; + } + } + + out << "}"; + return out; +} + +// Verify that the signo handler is handler. +int CheckSigHandler(uint32_t signo, uintptr_t handler) { + struct sigaction sa; + int ret = sigaction(signo, nullptr, &sa); + if (ret < 0) { + perror("sigaction"); + return 1; + } + + if (reinterpret_cast(handler) != sa.sa_handler) { + std::cerr << "signo " << signo << " handler got: " << sa.sa_handler + << " expected: " << std::hex << handler; + return 1; + } + return 0; +} + +// Verify that the signo is blocked. +int CheckSigBlocked(uint32_t signo) { + sigset_t s; + int ret = sigprocmask(SIG_SETMASK, nullptr, &s); + if (ret < 0) { + perror("sigprocmask"); + return 1; + } + + if (!sigismember(&s, signo)) { + std::cerr << "signal " << signo << " not blocked in signal mask: " << s + << std::endl; + return 1; + } + return 0; +} + +// Verify that the itimer is enabled. +int CheckItimerEnabled(uint32_t timer) { + struct itimerval itv; + int ret = getitimer(timer, &itv); + if (ret < 0) { + perror("getitimer"); + return 1; + } + + if (!itv.it_value.tv_sec && !itv.it_value.tv_usec && + !itv.it_interval.tv_sec && !itv.it_interval.tv_usec) { + std::cerr << "timer " << timer + << " not enabled. value sec: " << itv.it_value.tv_sec + << " usec: " << itv.it_value.tv_usec + << " interval sec: " << itv.it_interval.tv_sec + << " usec: " << itv.it_interval.tv_usec << std::endl; + return 1; + } + return 0; +} + +int PrintExecFn() { + unsigned long execfn = getauxval(AT_EXECFN); + if (!execfn) { + std::cerr << "AT_EXECFN missing" << std::endl; + return 1; + } + + std::cerr << reinterpret_cast(execfn) << std::endl; + return 0; +} + +int PrintExecName() { + const size_t name_length = 20; + char name[name_length] = {0}; + if (prctl(PR_GET_NAME, name) < 0) { + std::cerr << "prctl(PR_GET_NAME) failed" << std::endl; + return 1; + } + + std::cerr << name << std::endl; + return 0; +} + +void usage(const std::string& prog) { + std::cerr << "usage:\n" + << "\t" << prog << " CheckSigHandler \n" + << "\t" << prog << " CheckSigBlocked \n" + << "\t" << prog << " CheckTimerDisabled \n" + << "\t" << prog << " PrintExecFn\n" + << "\t" << prog << " PrintExecName" << std::endl; +} + +int main(int argc, char** argv) { + if (argc < 2) { + usage(argv[0]); + return 1; + } + + std::string func(argv[1]); + + if (func == "CheckSigHandler") { + if (argc != 4) { + usage(argv[0]); + return 1; + } + + char* end; + uint32_t signo = strtoul(argv[2], &end, 10); + if (end == argv[2]) { + std::cerr << "invalid signo: " << argv[2] << std::endl; + return 1; + } + + uintptr_t handler = strtoull(argv[3], &end, 16); + if (end == argv[3]) { + std::cerr << "invalid handler: " << std::hex << argv[3] << std::endl; + return 1; + } + + return CheckSigHandler(signo, handler); + } + + if (func == "CheckSigBlocked") { + if (argc != 3) { + usage(argv[0]); + return 1; + } + + char* end; + uint32_t signo = strtoul(argv[2], &end, 10); + if (end == argv[2]) { + std::cerr << "invalid signo: " << argv[2] << std::endl; + return 1; + } + + return CheckSigBlocked(signo); + } + + if (func == "CheckItimerEnabled") { + if (argc != 3) { + usage(argv[0]); + return 1; + } + + char* end; + uint32_t timer = strtoul(argv[2], &end, 10); + if (end == argv[2]) { + std::cerr << "invalid signo: " << argv[2] << std::endl; + return 1; + } + + return CheckItimerEnabled(timer); + } + + if (func == "PrintExecFn") { + // N.B. This will be called as an interpreter script, with the script passed + // as the third argument. We don't care about that script. + return PrintExecFn(); + } + + if (func == "PrintExecName") { + // N.B. This may be called as an interpreter script like PrintExecFn. + return PrintExecName(); + } + + std::cerr << "Invalid function: " << func << std::endl; + return 1; +} diff --git a/test/syscalls/linux/exit.cc b/test/syscalls/linux/exit.cc new file mode 100644 index 000000000..7246a7b3b --- /dev/null +++ b/test/syscalls/linux/exit.cc @@ -0,0 +1,77 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +void TestExit(int code) { + pid_t pid = fork(); + if (pid == 0) { + _exit(code); + } + + ASSERT_THAT(pid, SyscallSucceeds()); + + int status; + EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == code) << status; +} + +TEST(ExitTest, Success) { TestExit(0); } + +TEST(ExitTest, Failure) { TestExit(1); } + +// This test ensures that a process's file descriptors are closed when it calls +// exit(). In order to test this, the parent tries to read from a pipe whose +// write end is held by the child. While the read is blocking, the child exits, +// which should cause the parent to read 0 bytes due to EOF. +TEST(ExitTest, CloseFds) { + int pipe_fds[2]; + ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); + + FileDescriptor read_fd(pipe_fds[0]); + FileDescriptor write_fd(pipe_fds[1]); + + pid_t pid = fork(); + if (pid == 0) { + read_fd.reset(); + + SleepSafe(absl::Seconds(10)); + + _exit(0); + } + + EXPECT_THAT(pid, SyscallSucceeds()); + + write_fd.reset(); + + char buf[10]; + EXPECT_THAT(ReadFd(read_fd.get(), buf, sizeof(buf)), + SyscallSucceedsWithValue(0)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/exit_script.sh b/test/syscalls/linux/exit_script.sh new file mode 100755 index 000000000..f014fcf99 --- /dev/null +++ b/test/syscalls/linux/exit_script.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Copyright 2018 Google LLC +# +# 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. + +if [ $# -ne 1 ]; then + echo "Usage: $0 exit_code" + exit 255 +fi + +exit $1 diff --git a/test/syscalls/linux/fadvise64.cc b/test/syscalls/linux/fadvise64.cc new file mode 100644 index 000000000..041e8b7b6 --- /dev/null +++ b/test/syscalls/linux/fadvise64.cc @@ -0,0 +1,72 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +TEST(FAdvise64Test, Basic) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + // fadvise64 is noop in gVisor, so just test that it succeeds. + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_NORMAL), + SyscallSucceeds()); + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_RANDOM), + SyscallSucceeds()); + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_SEQUENTIAL), + SyscallSucceeds()); + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_WILLNEED), + SyscallSucceeds()); + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_DONTNEED), + SyscallSucceeds()); + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_NOREUSE), + SyscallSucceeds()); +} + +TEST(FAdvise64Test, InvalidArgs) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + // Note: offset is allowed to be negative. + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, static_cast(-1), + POSIX_FADV_NORMAL), + SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, 12345), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(FAdvise64Test, NoPipes) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + const FileDescriptor read(fds[0]); + const FileDescriptor write(fds[1]); + + ASSERT_THAT(syscall(__NR_fadvise64, read.get(), 0, 10, POSIX_FADV_NORMAL), + SyscallFailsWithErrno(ESPIPE)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fallocate.cc b/test/syscalls/linux/fallocate.cc new file mode 100644 index 000000000..53aedd4e4 --- /dev/null +++ b/test/syscalls/linux/fallocate.cc @@ -0,0 +1,57 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// These tests are very rudimentary because fallocate is not +// implemented. We just want to make sure the expected error codes are +// returned. + +TEST(FallocateTest, NotImplemented) { + auto temp_path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_path.path(), O_RDWR)); + + // Test that a completely unassigned fallocate mode returns EOPNOTSUPP. + ASSERT_THAT(fallocate(fd.get(), 0x80, 0, 32768), + SyscallFailsWithErrno(EOPNOTSUPP)); +} + +TEST(FallocateTest, BadOffset) { + auto temp_path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_path.path(), O_RDWR)); + ASSERT_THAT(fallocate(fd.get(), 0, -1, 32768), SyscallFailsWithErrno(EINVAL)); +} + +TEST(FallocateTest, BadLength) { + auto temp_path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_path.path(), O_RDWR)); + ASSERT_THAT(fallocate(fd.get(), 0, 0, -1), SyscallFailsWithErrno(EINVAL)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fault.cc b/test/syscalls/linux/fault.cc new file mode 100644 index 000000000..cfa7d0d1f --- /dev/null +++ b/test/syscalls/linux/fault.cc @@ -0,0 +1,71 @@ +// Copyright 2018 Google LLC +// +// 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. + +#define _GNU_SOURCE 1 +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +__attribute__((noinline)) void Fault(void) { + volatile int* foo = nullptr; + *foo = 0; +} + +int GetPcFromUcontext(ucontext_t* uc, uintptr_t* pc) { +#if defined(__x86_64__) + *pc = uc->uc_mcontext.gregs[REG_RIP]; + return 1; +#elif defined(__i386__) + *pc = uc->uc_mcontext.gregs[REG_EIP]; + return 1; +#else + return 0; +#endif +} + +void sigact_handler(int sig, siginfo_t* siginfo, void* context) { + uintptr_t pc; + if (GetPcFromUcontext(reinterpret_cast(context), &pc)) { + /* Expect Fault() to be at most 64 bytes in size. */ + uintptr_t fault_addr = reinterpret_cast(&Fault); + EXPECT_GE(pc, fault_addr); + EXPECT_LT(pc, fault_addr + 64); + exit(0); + } +} + +TEST(FaultTest, InRange) { + // Reset the signal handler to do nothing so that it doesn't freak out + // the test runner when we fire an alarm. + struct sigaction sa = {}; + sa.sa_sigaction = sigact_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + ASSERT_THAT(sigaction(SIGSEGV, &sa, nullptr), SyscallSucceeds()); + + Fault(); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fchdir.cc b/test/syscalls/linux/fchdir.cc new file mode 100644 index 000000000..2b13e36c3 --- /dev/null +++ b/test/syscalls/linux/fchdir.cc @@ -0,0 +1,77 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(FchdirTest, Success) { + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + int fd; + ASSERT_THAT(fd = open(temp_dir.path().c_str(), O_DIRECTORY | O_RDONLY), + SyscallSucceeds()); + + EXPECT_THAT(fchdir(fd), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + // Change CWD to a permanent location as temp dirs will be cleaned up. + EXPECT_THAT(chdir("/"), SyscallSucceeds()); +} + +TEST(FchdirTest, InvalidFD) { + EXPECT_THAT(fchdir(-1), SyscallFailsWithErrno(EBADF)); +} + +TEST(FchdirTest, PermissionDenied) { + // Drop capabilities that allow us to override directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); + + int fd; + ASSERT_THAT(fd = open(temp_dir.path().c_str(), O_DIRECTORY | O_RDONLY), + SyscallSucceeds()); + + EXPECT_THAT(fchdir(fd), SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(FchdirTest, NotDir) { + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + int fd; + ASSERT_THAT(fd = open(temp_file.path().c_str(), O_CREAT | O_RDONLY, 0777), + SyscallSucceeds()); + + EXPECT_THAT(fchdir(fd), SyscallFailsWithErrno(ENOTDIR)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc new file mode 100644 index 000000000..355334bfa --- /dev/null +++ b/test/syscalls/linux/fcntl.cc @@ -0,0 +1,978 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/cleanup.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/timer_util.h" + +DEFINE_string(child_setlock_on, "", + "Contains the path to try to set a file lock on."); +DEFINE_bool(child_setlock_write, false, + "Whether to set a writable lock (otherwise readable)"); +DEFINE_bool(blocking, false, + "Whether to set a blocking lock (otherwise non-blocking)."); +DEFINE_bool(retry_eintr, false, "Whether to retry in the subprocess on EINTR."); +DEFINE_uint64(child_setlock_start, 0, "The value of struct flock start"); +DEFINE_uint64(child_setlock_len, 0, "The value of struct flock len"); +DEFINE_int32(socket_fd, -1, + "A socket to use for communicating more state back " + "to the parent."); + +namespace gvisor { +namespace testing { + +// O_LARGEFILE as defined by Linux. glibc tries to be clever by setting it to 0 +// because "it isn't needed", even though Linux can return it via F_GETFL. +constexpr int kOLargeFile = 00100000; + +class FcntlLockTest : public ::testing::Test { + public: + void SetUp() override { + // Let's make a socket pair. + ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, fds_), SyscallSucceeds()); + } + + void TearDown() override { + EXPECT_THAT(close(fds_[0]), SyscallSucceeds()); + EXPECT_THAT(close(fds_[1]), SyscallSucceeds()); + } + + int64_t GetSubprocessFcntlTimeInUsec() { + int64_t ret = 0; + EXPECT_THAT(ReadFd(fds_[0], reinterpret_cast(&ret), sizeof(ret)), + SyscallSucceedsWithValue(sizeof(ret))); + return ret; + } + + // The first fd will remain with the process creating the subprocess + // and the second will go to the subprocess. + int fds_[2] = {}; +}; + +namespace { + +PosixErrorOr SubprocessLock(std::string const& path, bool for_write, + bool blocking, bool retry_eintr, int fd, + off_t start, off_t length, pid_t* child) { + std::vector args = { + "/proc/self/exe", "--child_setlock_on", path, + "--child_setlock_start", absl::StrCat(start), "--child_setlock_len", + absl::StrCat(length), "--socket_fd", absl::StrCat(fd)}; + + if (for_write) { + args.push_back("--child_setlock_write"); + } + + if (blocking) { + args.push_back("--blocking"); + } + + if (retry_eintr) { + args.push_back("--retry_eintr"); + } + + int execve_errno = 0; + ASSIGN_OR_RETURN_ERRNO( + auto cleanup, + ForkAndExec("/proc/self/exe", ExecveArray(args.begin(), args.end()), {}, + nullptr, child, &execve_errno)); + + if (execve_errno != 0) { + return PosixError(execve_errno, "execve"); + } + + return std::move(cleanup); +} + +PosixErrorOr Eventfd(int count, int flags) { + int efd = eventfd(count, flags); + if (efd < 0) { + return PosixError(errno, "Eventfd"); + } + return FileDescriptor(efd); +} + +TEST(FcntlTest, SetCloExec) { + // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Eventfd(0, 0)); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); + + // Set the FD_CLOEXEC flag. + ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); +} + +TEST(FcntlTest, ClearCloExec) { + // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set. + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Eventfd(0, EFD_CLOEXEC)); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); + + // Clear the FD_CLOEXEC flag. + ASSERT_THAT(fcntl(fd.get(), F_SETFD, 0), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); +} + +TEST(FcntlTest, IndependentDescriptorFlags) { + // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Eventfd(0, 0)); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); + + // Duplicate the descriptor. Ensure that it also doesn't have FD_CLOEXEC. + FileDescriptor newfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); + ASSERT_THAT(fcntl(newfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); + + // Set FD_CLOEXEC on the first FD. + ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); + + // Ensure that the second FD is unaffected by the change on the first. + ASSERT_THAT(fcntl(newfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); +} + +// All file description flags passed to open appear in F_GETFL. +TEST(FcntlTest, GetAllFlags) { + TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + int flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND; + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), flags)); + + // Linux forces O_LARGEFILE on all 64-bit kernels and gVisor's is 64-bit. + int expected = flags | kOLargeFile; + + int rflags; + EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(rflags, expected); +} + +TEST(FcntlTest, SetFlags) { + TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), 0)); + + int const flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND; + EXPECT_THAT(fcntl(fd.get(), F_SETFL, flags), SyscallSucceeds()); + + // Can't set O_RDWR or O_SYNC. + // Linux forces O_LARGEFILE on all 64-bit kernels and gVisor's is 64-bit. + int expected = O_DIRECT | O_NONBLOCK | O_APPEND | kOLargeFile; + + int rflags; + EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(rflags, expected); +} + +TEST_F(FcntlLockTest, SetLockBadFd) { + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // len 0 has a special meaning: lock all bytes despite how + // large the file grows. + fl.l_len = 0; + EXPECT_THAT(fcntl(-1, F_SETLK, &fl), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(FcntlLockTest, SetLockPipe) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd, but doesn't matter, we expect this to fail. + fl.l_len = 0; + EXPECT_THAT(fcntl(fds[0], F_SETLK, &fl), SyscallFailsWithErrno(EBADF)); + EXPECT_THAT(close(fds[0]), SyscallSucceeds()); + EXPECT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(FcntlLockTest, SetLockDir) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); +} + +TEST_F(FcntlLockTest, SetLockBadOpenFlagsWrite) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY, 0666)); + + struct flock fl0; + fl0.l_type = F_WRLCK; + fl0.l_whence = SEEK_SET; + fl0.l_start = 0; + // Same as SetLockBadFd. + fl0.l_len = 0; + + // Expect that setting a write lock using a read only file descriptor + // won't work. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(FcntlLockTest, SetLockBadOpenFlagsRead) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY, 0666)); + + struct flock fl1; + fl1.l_type = F_RDLCK; + fl1.l_whence = SEEK_SET; + fl1.l_start = 0; + // Same as SetLockBadFd. + fl1.l_len = 0; + + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl1), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(FcntlLockTest, SetLockUnlockOnNothing) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); +} + +TEST_F(FcntlLockTest, SetWriteLockSingleProc) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd0 = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + EXPECT_THAT(fcntl(fd0.get(), F_SETLK, &fl), SyscallSucceeds()); + // Expect to be able to take the same lock on the same fd no problem. + EXPECT_THAT(fcntl(fd0.get(), F_SETLK, &fl), SyscallSucceeds()); + + FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + // Expect to be able to take the same lock from a different fd but for + // the same process. + EXPECT_THAT(fcntl(fd1.get(), F_SETLK, &fl), SyscallSucceeds()); +} + +TEST_F(FcntlLockTest, SetReadLockMultiProc) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // spawn a child process to take a read lock on the same file. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), false /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetReadThenWriteLockMultiProc) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Assert that another process trying to lock on the same file will fail + // with EAGAIN. It's important that we keep the fd above open so that + // that the other process will contend with the lock. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), true /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) + << "Exited with code: " << status; + + // Close the fd: we want to test that another process can acquire the + // lock after this point. + fd.reset(); + // Assert that another process can now acquire the lock. + + child_pid = 0; + auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), true /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetWriteThenReadLockMultiProc) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + // Same as SetReadThenWriteLockMultiProc. + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + // Same as SetReadThenWriteLockMultiProc. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Same as SetReadThenWriteLockMultiProc. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), false /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) + << "Exited with code: " << status; + + // Same as SetReadThenWriteLockMultiProc. + fd.reset(); // Close the fd. + + // Same as SetReadThenWriteLockMultiProc. + child_pid = 0; + auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), false /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetWriteLockMultiProc) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + // Same as SetReadThenWriteLockMultiProc. + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + // Same as SetReadWriteLockMultiProc. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Same as SetReadWriteLockMultiProc. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), true /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) + << "Exited with code: " << status; + + fd.reset(); // Close the FD. + // Same as SetReadWriteLockMultiProc. + child_pid = 0; + auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), true /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetLockIsRegional) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 4096; + + // Same as SetReadWriteLockMultiProc. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Same as SetReadWriteLockMultiProc. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), true /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_len, 0, &child_pid)); + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetLockUpgradeDowngrade) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + // Same as SetReadWriteLockMultiProc. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Upgrade to a write lock. This will prevent anyone else from taking + // the lock. + fl.l_type = F_WRLCK; + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Same as SetReadWriteLockMultiProc., + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), false /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) + << "Exited with code: " << status; + + // Downgrade back to a read lock. + fl.l_type = F_RDLCK; + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Do the same stint as before, but this time it should succeed. + child_pid = 0; + auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), false /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetLockDroppedOnClose) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + // While somewhat surprising, obtaining another fd to the same file and + // then closing it in this process drops *all* locks. + FileDescriptor other_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + // Same as SetReadThenWriteLockMultiProc. + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + // Same as SetReadWriteLockMultiProc. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + other_fd.reset(); // Close. + + // Expect to be able to get the lock, given that the close above dropped it. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(file.path(), true /* write lock */, + false /* nonblocking */, false /* no eintr retry */, + -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetLockUnlock) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + // Setup two regional locks with different permissions. + struct flock fl0; + fl0.l_type = F_WRLCK; + fl0.l_whence = SEEK_SET; + fl0.l_start = 0; + fl0.l_len = 4096; + + struct flock fl1; + fl1.l_type = F_RDLCK; + fl1.l_whence = SEEK_SET; + fl1.l_start = 4096; + // Same as SetLockBadFd. + fl1.l_len = 0; + + // Set both region locks. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallSucceeds()); + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl1), SyscallSucceeds()); + + // Another process should fail to take a read lock on the entire file + // due to the regional write lock. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( + file.path(), false /* write lock */, false /* nonblocking */, + false /* no eintr retry */, -1 /* no socket fd */, 0, 0, &child_pid)); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) + << "Exited with code: " << status; + + // Then only unlock the writable one. This should ensure that other + // processes can take any read lock that it wants. + fl0.l_type = F_UNLCK; + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallSucceeds()); + + // Another process should now succeed to get a read lock on the entire file. + child_pid = 0; + auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( + file.path(), false /* write lock */, false /* nonblocking */, + false /* no eintr retry */, -1 /* no socket fd */, 0, 0, &child_pid)); + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST_F(FcntlLockTest, SetLockAcrossRename) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + // Setup two regional locks with different permissions. + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + // Same as SetLockBadFd. + fl.l_len = 0; + + // Set the region lock. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + // Rename the file to someplace nearby. + std::string const newpath = NewTempAbsPath(); + EXPECT_THAT(rename(file.path().c_str(), newpath.c_str()), SyscallSucceeds()); + + // Another process should fail to take a read lock on the renamed file + // since we still have an open handle to the inode. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + SubprocessLock(newpath, false /* write lock */, false /* nonblocking */, + false /* no eintr retry */, -1 /* no socket fd */, + fl.l_start, fl.l_len, &child_pid)); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) + << "Exited with code: " << status; +} + +// NOTE: The blocking tests below aren't perfect. It's hard to assert exactly +// what the kernel did while handling a syscall. These tests are timing based +// because there really isn't any other reasonable way to assert that correct +// blocking behavior happened. + +// This test will verify that blocking works as expected when another process +// holds a write lock when obtaining a write lock. This test will hold the lock +// for some amount of time and then wait for the second process to send over the +// socket_fd the amount of time it was blocked for before the lock succeeded. +TEST_F(FcntlLockTest, SetWriteLockThenBlockingWriteLock) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + // Take the write lock. + ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); + + // Attempt to take the read lock in a sub process. This will immediately block + // so we will release our lock after some amount of time and then assert the + // amount of time the other process was blocked for. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( + file.path(), true /* write lock */, true /* Blocking Lock */, + true /* Retry on EINTR */, fds_[1] /* Socket fd for timing information */, + fl.l_start, fl.l_len, &child_pid)); + + // We will wait kHoldLockForSec before we release our lock allowing the + // subprocess to obtain it. + constexpr absl::Duration kHoldLockFor = absl::Seconds(5); + const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); + + absl::SleepFor(kHoldLockFor); + + // Unlock our write lock. + fl.l_type = F_UNLCK; + ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); + + // Read the blocked time from the subprocess socket. + int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); + + // We must have been waiting at least kMinBlockTime. + EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); + + // The FCNTL write lock must always succeed as it will simply block until it + // can obtain the lock. + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +// This test will veirfy that blocking works as expected when another process +// holds a read lock when obtaining a write lock. This test will hold the lock +// for some amount of time and then wait for the second process to send over the +// socket_fd the amount of time it was blocked for before the lock succeeded. +TEST_F(FcntlLockTest, SetReadLockThenBlockingWriteLock) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + // Take the write lock. + ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); + + // Attempt to take the read lock in a sub process. This will immediately block + // so we will release our lock after some amount of time and then assert the + // amount of time the other process was blocked for. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( + file.path(), true /* write lock */, true /* Blocking Lock */, + true /* Retry on EINTR */, fds_[1] /* Socket fd for timing information */, + fl.l_start, fl.l_len, &child_pid)); + + // We will wait kHoldLockForSec before we release our lock allowing the + // subprocess to obtain it. + constexpr absl::Duration kHoldLockFor = absl::Seconds(5); + + const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); + + absl::SleepFor(kHoldLockFor); + + // Unlock our READ lock. + fl.l_type = F_UNLCK; + ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); + + // Read the blocked time from the subprocess socket. + int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); + + // We must have been waiting at least kMinBlockTime. + EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); + + // The FCNTL write lock must always succeed as it will simply block until it + // can obtain the lock. + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +// This test will veirfy that blocking works as expected when another process +// holds a write lock when obtaining a read lock. This test will hold the lock +// for some amount of time and then wait for the second process to send over the +// socket_fd the amount of time it was blocked for before the lock succeeded. +TEST_F(FcntlLockTest, SetWriteLockThenBlockingReadLock) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + // Take the write lock. + ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); + + // Attempt to take the read lock in a sub process. This will immediately block + // so we will release our lock after some amount of time and then assert the + // amount of time the other process was blocked for. + pid_t child_pid = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( + file.path(), false /* read lock */, true /* Blocking Lock */, + true /* Retry on EINTR */, fds_[1] /* Socket fd for timing information */, + fl.l_start, fl.l_len, &child_pid)); + + // We will wait kHoldLockForSec before we release our lock allowing the + // subprocess to obtain it. + constexpr absl::Duration kHoldLockFor = absl::Seconds(5); + + const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); + + absl::SleepFor(kHoldLockFor); + + // Unlock our write lock. + fl.l_type = F_UNLCK; + ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); + + // Read the blocked time from the subprocess socket. + int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); + + // We must have been waiting at least kMinBlockTime. + EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); + + // The FCNTL read lock must always succeed as it will simply block until it + // can obtain the lock. + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +// This test will verify that when one process only holds a read lock that +// another will not block while obtaining a read lock when F_SETLKW is used. +TEST_F(FcntlLockTest, SetReadLockThenBlockingReadLock) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + // Take the READ lock. + ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); + + // Attempt to take the read lock in a sub process. Since multiple processes + // can hold a read lock this should immediately return without blocking + // even though we used F_SETLKW in the subprocess. + pid_t child_pid = 0; + auto sp = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( + file.path(), false /* read lock */, true /* Blocking Lock */, + true /* Retry on EINTR */, -1 /* No fd, should not block */, fl.l_start, + fl.l_len, &child_pid)); + + // We never release the lock and the subprocess should still obtain it without + // blocking for any period of time. + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +TEST(FcntlTest, GetO_ASYNC) { + FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + int flag_fl = -1; + ASSERT_THAT(flag_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(flag_fl & O_ASYNC, 0); + + int flag_fd = -1; + ASSERT_THAT(flag_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); + EXPECT_EQ(flag_fd & O_ASYNC, 0); +} + +TEST(FcntlTest, SetFlO_ASYNC) { + FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + int before_fl = -1; + ASSERT_THAT(before_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + + int before_fd = -1; + ASSERT_THAT(before_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); + + ASSERT_THAT(fcntl(s.get(), F_SETFL, before_fl | O_ASYNC), SyscallSucceeds()); + + int after_fl = -1; + ASSERT_THAT(after_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(after_fl, before_fl | O_ASYNC); + + int after_fd = -1; + ASSERT_THAT(after_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); + EXPECT_EQ(after_fd, before_fd); +} + +TEST(FcntlTest, SetFdO_ASYNC) { + FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + int before_fl = -1; + ASSERT_THAT(before_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + + int before_fd = -1; + ASSERT_THAT(before_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); + + ASSERT_THAT(fcntl(s.get(), F_SETFD, before_fd | O_ASYNC), SyscallSucceeds()); + + int after_fl = -1; + ASSERT_THAT(after_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(after_fl, before_fl); + + int after_fd = -1; + ASSERT_THAT(after_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); + EXPECT_EQ(after_fd, before_fd); +} + +TEST(FcntlTest, DupAfterO_ASYNC) { + FileDescriptor s1 = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + int before = -1; + ASSERT_THAT(before = fcntl(s1.get(), F_GETFL), SyscallSucceeds()); + + ASSERT_THAT(fcntl(s1.get(), F_SETFL, before | O_ASYNC), SyscallSucceeds()); + + FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(s1.Dup()); + + int after = -1; + ASSERT_THAT(after = fcntl(fd2.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(after & O_ASYNC, O_ASYNC); +} + +TEST(FcntlTest, GetOwn) { + FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), + SyscallSucceedsWithValue(0)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + gvisor::testing::TestInit(&argc, &argv); + + if (!FLAGS_child_setlock_on.empty()) { + int socket_fd = FLAGS_socket_fd; + int fd = open(FLAGS_child_setlock_on.c_str(), O_RDWR, 0666); + if (fd == -1 && errno != 0) { + int err = errno; + std::cerr << "CHILD open " << FLAGS_child_setlock_on << " failed " << err + << std::endl; + exit(err); + } + + struct flock fl; + if (FLAGS_child_setlock_write) { + fl.l_type = F_WRLCK; + } else { + fl.l_type = F_RDLCK; + } + fl.l_whence = SEEK_SET; + fl.l_start = FLAGS_child_setlock_start; + fl.l_len = FLAGS_child_setlock_len; + + // Test the fcntl, no need to log, the error is unambiguously + // from fcntl at this point. + int err = 0; + int ret = 0; + + gvisor::testing::MonotonicTimer timer; + timer.Start(); + do { + ret = fcntl(fd, FLAGS_blocking ? F_SETLKW : F_SETLK, &fl); + } while (FLAGS_retry_eintr && ret == -1 && errno == EINTR); + auto usec = absl::ToInt64Microseconds(timer.Duration()); + + if (ret == -1 && errno != 0) { + err = errno; + } + + // If there is a socket fd let's send back the time in microseconds it took + // to execute this syscall. + if (socket_fd != -1) { + gvisor::testing::WriteFd(socket_fd, reinterpret_cast(&usec), + sizeof(usec)); + close(socket_fd); + } + + close(fd); + exit(err); + } + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/file_base.h b/test/syscalls/linux/file_base.h new file mode 100644 index 000000000..19c9a5053 --- /dev/null +++ b/test/syscalls/linux/file_base.h @@ -0,0 +1,206 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_FILE_BASE_H_ +#define GVISOR_TEST_SYSCALLS_FILE_BASE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +class FileTest : public ::testing::Test { + public: + void SetUp() override { + test_pipe_[0] = -1; + test_pipe_[1] = -1; + + test_file_name_ = NewTempAbsPath(); + test_file_fd_ = ASSERT_NO_ERRNO_AND_VALUE( + Open(test_file_name_, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)); + + // FIXME: enable when mknod syscall is supported. + // test_fifo_name_ = NewTempAbsPath(); + // ASSERT_THAT(mknod(test_fifo_name_.c_str()), S_IFIFO|0644, 0, + // SyscallSucceeds()); + // ASSERT_THAT(test_fifo_[1] = open(test_fifo_name_.c_str(), + // O_WRONLY), + // SyscallSucceeds()); + // ASSERT_THAT(test_fifo_[0] = open(test_fifo_name_.c_str(), + // O_RDONLY), + // SyscallSucceeds()); + + ASSERT_THAT(pipe(test_pipe_), SyscallSucceeds()); + ASSERT_THAT(fcntl(test_pipe_[0], F_SETFL, O_NONBLOCK), SyscallSucceeds()); + } + + // CloseFile will allow the test to manually close the file descriptor. + void CloseFile() { test_file_fd_.reset(); } + + // UnlinkFile will allow the test to manually unlink the file. + void UnlinkFile() { + if (!test_file_name_.empty()) { + EXPECT_THAT(unlink(test_file_name_.c_str()), SyscallSucceeds()); + test_file_name_.clear(); + } + } + + // ClosePipes will allow the test to manually close the pipes. + void ClosePipes() { + if (test_pipe_[0] > 0) { + EXPECT_THAT(close(test_pipe_[0]), SyscallSucceeds()); + } + + if (test_pipe_[1] > 0) { + EXPECT_THAT(close(test_pipe_[1]), SyscallSucceeds()); + } + + test_pipe_[0] = -1; + test_pipe_[1] = -1; + } + + void TearDown() override { + CloseFile(); + UnlinkFile(); + ClosePipes(); + + // FIXME: enable when mknod syscall is supported. + // close(test_fifo_[0]); + // close(test_fifo_[1]); + // unlink(test_fifo_name_.c_str()); + } + + std::string test_file_name_; + std::string test_fifo_name_; + FileDescriptor test_file_fd_; + + int test_fifo_[2]; + int test_pipe_[2]; +}; + +class SocketTest : public ::testing::Test { + public: + void SetUp() override { + test_unix_stream_socket_[0] = -1; + test_unix_stream_socket_[1] = -1; + test_unix_dgram_socket_[0] = -1; + test_unix_dgram_socket_[1] = -1; + test_unix_seqpacket_socket_[0] = -1; + test_unix_seqpacket_socket_[1] = -1; + test_tcp_socket_[0] = -1; + test_tcp_socket_[1] = -1; + + ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, test_unix_stream_socket_), + SyscallSucceeds()); + ASSERT_THAT(fcntl(test_unix_stream_socket_[0], F_SETFL, O_NONBLOCK), + SyscallSucceeds()); + ASSERT_THAT(socketpair(AF_UNIX, SOCK_DGRAM, 0, test_unix_dgram_socket_), + SyscallSucceeds()); + ASSERT_THAT(fcntl(test_unix_dgram_socket_[0], F_SETFL, O_NONBLOCK), + SyscallSucceeds()); + ASSERT_THAT( + socketpair(AF_UNIX, SOCK_SEQPACKET, 0, test_unix_seqpacket_socket_), + SyscallSucceeds()); + ASSERT_THAT(fcntl(test_unix_seqpacket_socket_[0], F_SETFL, O_NONBLOCK), + SyscallSucceeds()); + } + + void TearDown() override { + close(test_unix_stream_socket_[0]); + close(test_unix_stream_socket_[1]); + + close(test_unix_dgram_socket_[0]); + close(test_unix_dgram_socket_[1]); + + close(test_unix_seqpacket_socket_[0]); + close(test_unix_seqpacket_socket_[1]); + + close(test_tcp_socket_[0]); + close(test_tcp_socket_[1]); + } + + int test_unix_stream_socket_[2]; + int test_unix_dgram_socket_[2]; + int test_unix_seqpacket_socket_[2]; + int test_tcp_socket_[2]; +}; + +// MatchesStringLength checks that a tuple argument of (struct iovec *, int) +// corresponding to an iovec array and its length, contains data that matches +// the std::string length strlen. +MATCHER_P(MatchesStringLength, strlen, "") { + struct iovec* iovs = arg.first; + int niov = arg.second; + int offset = 0; + for (int i = 0; i < niov; i++) { + offset += iovs[i].iov_len; + } + if (offset != static_cast(strlen)) { + *result_listener << offset; + return false; + } + return true; +} + +// MatchesStringValue checks that a tuple argument of (struct iovec *, int) +// corresponding to an iovec array and its length, contains data that matches +// the std::string value str. +MATCHER_P(MatchesStringValue, str, "") { + struct iovec* iovs = arg.first; + int len = strlen(str); + int niov = arg.second; + int offset = 0; + for (int i = 0; i < niov; i++) { + struct iovec iov = iovs[i]; + if (len < offset) { + *result_listener << "strlen " << len << " < offset " << offset; + return false; + } + if (strncmp(static_cast(iov.iov_base), &str[offset], iov.iov_len)) { + absl::string_view iovec_string(static_cast(iov.iov_base), + iov.iov_len); + *result_listener << iovec_string << " @offset " << offset; + return false; + } + offset += iov.iov_len; + } + return true; +} + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_FILE_BASE_H_ diff --git a/test/syscalls/linux/flock.cc b/test/syscalls/linux/flock.cc new file mode 100644 index 000000000..fb93c8034 --- /dev/null +++ b/test/syscalls/linux/flock.cc @@ -0,0 +1,588 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/file_base.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class FlockTest : public FileTest {}; + +TEST_F(FlockTest, BadFD) { + // EBADF: fd is not an open file descriptor. + ASSERT_THAT(flock(-1, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(FlockTest, InvalidOpCombinations) { + // The operation cannot be both exclusive and shared. + EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_SH | LOCK_NB), + SyscallFailsWithErrno(EINVAL)); + + // Locking and Unlocking doesn't make sense. + EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_UN | LOCK_NB), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_UN | LOCK_NB), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(FlockTest, NoOperationSpecified) { + // Not specifying an operation is invalid. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(FlockTestNoFixture, FlockSupportsPipes) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + EXPECT_THAT(flock(fds[0], LOCK_EX | LOCK_NB), SyscallSucceeds()); + EXPECT_THAT(close(fds[0]), SyscallSucceeds()); + EXPECT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(FlockTest, TestSimpleExLock) { + // Test that we can obtain an exclusive lock (no other holders) + // and that we can unlock it. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestSimpleShLock) { + // Test that we can obtain a shared lock (no other holders) + // and that we can unlock it. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestLockableAnyMode) { + // flock(2): A shared or exclusive lock can be placed on a file + // regardless of the mode in which the file was opened. + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(test_file_name_, O_RDONLY)); // open read only to test + + // Mode shouldn't prevent us from taking an exclusive lock. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); + + // Unlock + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestUnlockWithNoHolders) { + // Test that unlocking when no one holds a lock succeeeds. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestRepeatedExLockingBySameHolder) { + // Test that repeated locking by the same holder for the + // same type of lock works correctly. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestRepeatedExLockingSingleUnlock) { + // Test that repeated locking by the same holder for the + // same type of lock works correctly and that a single unlock is required. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); + + // Should be unlocked at this point + ASSERT_THAT(flock(fd.get(), LOCK_NB | LOCK_EX), SyscallSucceedsWithValue(0)); + + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestRepeatedShLockingBySameHolder) { + // Test that repeated locking by the same holder for the + // same type of lock works correctly. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestSingleHolderUpgrade) { + // Test that a shared lock is upgradable when no one else holds a lock. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestSingleHolderDowngrade) { + // Test single holder lock downgrade case. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestMultipleShared) { + // This is a simple test to verify that multiple independent shared + // locks will be granted. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // A shared lock should be granted as there only exists other shared locks. + ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); + + // Unlock both. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +/* + * flock(2): If a process uses open(2) (or similar) to obtain more than one + * descriptor for the same file, these descriptors are treated + * independently by flock(). An attempt to lock the file using one of + * these file descriptors may be denied by a lock that the calling process + * has already placed via another descriptor. + */ +TEST_F(FlockTest, TestMultipleHolderSharedExclusive) { + // This test will verify that an exclusive lock will not be granted + // while a shared is held. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Verify We're unable to get an exlcusive lock via the second FD. + // because someone is holding a shared lock. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Unlock + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestSharedLockFailExclusiveHolder) { + // This test will verify that a shared lock is denied while + // someone holds an exclusive lock. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Verify we're unable to get an shared lock via the second FD. + // because someone is holding an exclusive lock. + ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Unlock + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolder) { + // This test will verify that an exclusive lock is denied while + // someone already holds an exclsuive lock. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Verify we're unable to get an exclusive lock via the second FD + // because someone is already holding an exclusive lock. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Unlock + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestMultipleHolderSharedExclusiveUpgrade) { + // This test will verify that we cannot obtain an exclusive lock while + // a shared lock is held by another descriptor, then verify that an upgrade + // is possible on a shared lock once all other shared locks have closed. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Verify we're unable to get an exclusive lock via the second FD because + // a shared lock is held. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Verify that we can get a shared lock via the second descriptor instead + ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); + + // Unlock the first and there will only be one shared lock remaining. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); + + // Upgrade 2nd fd. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); + + // Finally unlock the second + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestMultipleHolderSharedExclusiveDowngrade) { + // This test will verify that a shared lock is not obtainable while an + // exclusive lock is held but that once the first is downgraded that + // the second independent file descriptor can also get a shared lock. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Verify We're unable to get a shared lock via the second FD because + // an exclusive lock is held. + ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Verify that we can downgrade the first. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + + // Now verify that we can obtain a shared lock since the first was downgraded. + ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); + + // Finally unlock both. + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +/* + * flock(2): Locks created by flock() are associated with an open file table + * entry. This means that duplicate file descriptors (created by, for example, + * fork(2) or dup(2)) refer to the same lock, and this lock may be modified or + * released using any of these descriptors. Furthermore, the lock is released + * either by an explicit LOCK_UN operation on any of these duplicate descriptors + * or when all such descriptors have been closed. + */ +TEST_F(FlockTest, TestDupFdUpgrade) { + // This test will verify that a shared lock is upgradeable via a dupped + // file descriptor, if the FD wasn't dupped this would fail. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); + + // Now we should be able to upgrade via the dupped fd. + ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + + // Validate unlock via dupped fd. + ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestDupFdDowngrade) { + // This test will verify that a exclusive lock is downgradable via a dupped + // file descriptor, if the FD wasn't dupped this would fail. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); + + // Now we should be able to downgrade via the dupped fd. + ASSERT_THAT(flock(dup_fd.get(), LOCK_SH | LOCK_NB), + SyscallSucceedsWithValue(0)); + + // Validate unlock via dupped fd + ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestDupFdCloseRelease) { + // flock(2): Furthermore, the lock is released either by an explicit LOCK_UN + // operation on any of these duplicate descriptors, or when all such + // descriptors have been closed. + // + // This test will verify that a dupped fd closing will not release the + // underlying lock until all such dupped fds have closed. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + + FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); + + // At this point we have ONE exclusive locked referenced by two different fds. + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Validate that we cannot get a lock on a new unrelated FD. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Closing the dupped fd shouldn't affect the lock until all are closed. + dup_fd.reset(); // Closed the duped fd. + + // Validate that we still cannot get a lock on a new unrelated FD. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Closing the first fd + CloseFile(); // Will validate the syscall succeeds. + + // Now we should actually be able to get a lock since all fds related to + // the first lock are closed. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); + + // Unlock. + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestDupFdUnlockRelease) { + /* flock(2): Furthermore, the lock is released either by an explicit LOCK_UN + * operation on any of these duplicate descriptors, or when all such + * descriptors have been closed. + */ + // This test will verify that an explict unlock on a dupped FD will release + // the underlying lock unlike the previous case where close on a dup was + // not enough to release the lock. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), + SyscallSucceedsWithValue(0)); + + const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); + + // At this point we have ONE exclusive locked referenced by two different fds. + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Validate that we cannot get a lock on a new unrelated FD. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Explicitly unlock via the dupped descriptor. + ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); + + // Validate that we can now get the lock since we explicitly unlocked. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); + + // Unlock + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +TEST_F(FlockTest, TestDupFdFollowedByLock) { + // This test will verify that taking a lock on a file descriptor that has + // already been dupped means that the lock is shared between both. This is + // slightly different than than duping on an already locked FD. + FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); + + // Take a lock. + ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); + + // Now dup_fd and test_file_ should both reference the same lock. + // We shouldn't be able to obtain a lock until both are closed. + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Closing the first fd + dup_fd.reset(); // Close the duped fd. + + // Validate that we cannot get a lock yet because the dupped descriptor. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Closing the second fd. + CloseFile(); // CloseFile() will validate the syscall succeeds. + + // Now we should be able to get the lock. + ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); + + // Unlock. + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); +} + +// NOTE: These blocking tests are not perfect. Unfortunantely it's very hard to +// determine if a thread was actually blocked in the kernel so we're forced +// to use timing. +TEST_F(FlockTest, BlockingLockNoBlockingForSharedLocks) { + // This test will verify that although LOCK_NB isn't specified + // two different fds can obtain shared locks without blocking. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds()); + + // kHoldLockTime is the amount of time we will hold the lock before releasing. + constexpr absl::Duration kHoldLockTime = absl::Seconds(30); + + const DisableSave ds; // Timing-related. + + // We do this in another thread so we can determine if it was actually + // blocked by timing the amount of time it took for the syscall to complete. + ScopedThread t([&] { + MonotonicTimer timer; + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // Only a single shared lock is held, the lock will be granted immediately. + // This should be granted without any blocking. Don't save here to avoid + // wild discrepencies on timing. + timer.Start(); + ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallSucceeds()); + + // We held the lock for 30 seconds but this thread should not have + // blocked at all so we expect a very small duration on syscall completion. + ASSERT_LT(timer.Duration(), + absl::Seconds(1)); // 1000ms is much less than 30s. + + // We can release our second shared lock + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); + }); + + // Sleep before unlocking. + absl::SleepFor(kHoldLockTime); + + // Release the first shared lock. Don't save in this situation to avoid + // discrepencies in timing. + EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); +} + +TEST_F(FlockTest, BlockingLockFirstSharedSecondExclusive) { + // This test will verify that if someone holds a shared lock any attempt to + // obtain an exclusive lock will result in blocking. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds()); + + // kHoldLockTime is the amount of time we will hold the lock before releasing. + constexpr absl::Duration kHoldLockTime = absl::Seconds(2); + + const DisableSave ds; // Timing-related. + + // We do this in another thread so we can determine if it was actually + // blocked by timing the amount of time it took for the syscall to complete. + ScopedThread t([&] { + MonotonicTimer timer; + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // This exclusive lock should block because someone is already holding a + // shared lock. We don't save here to avoid wild discrepencies on timing. + timer.Start(); + ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds()); + + // We should be blocked, we will expect to be blocked for more than 1.0s. + ASSERT_GT(timer.Duration(), absl::Seconds(1)); + + // We can release our exclusive lock. + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); + }); + + // Sleep before unlocking. + absl::SleepFor(kHoldLockTime); + + // Release the shared lock allowing the thread to proceed. + // We don't save here to avoid wild discrepencies in timing. + EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); +} + +TEST_F(FlockTest, BlockingLockFirstExclusiveSecondShared) { + // This test will verify that if someone holds an exclusive lock any attempt + // to obtain a shared lock will result in blocking. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds()); + + // kHoldLockTime is the amount of time we will hold the lock before releasing. + constexpr absl::Duration kHoldLockTime = absl::Seconds(2); + + const DisableSave ds; // Timing-related. + + // We do this in another thread so we can determine if it was actually + // blocked by timing the amount of time it took for the syscall to complete. + ScopedThread t([&] { + MonotonicTimer timer; + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // This shared lock should block because someone is already holding an + // exclusive lock. We don't save here to avoid wild discrepencies on timing. + timer.Start(); + ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_SH), SyscallSucceeds()); + + // We should be blocked, we will expect to be blocked for more than 1.0s. + ASSERT_GT(timer.Duration(), absl::Seconds(1)); + + // We can release our shared lock. + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); + }); + + // Sleep before unlocking. + absl::SleepFor(kHoldLockTime); + + // Release the exclusive lock allowing the blocked thread to proceed. + // We don't save here to avoid wild discrepencies in timing. + EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); +} + +TEST_F(FlockTest, BlockingLockFirstExclusiveSecondExclusive) { + // This test will verify that if someone holds an exclusive lock any attempt + // to obtain another exclusive lock will result in blocking. + ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds()); + + // kHoldLockTime is the amount of time we will hold the lock before releasing. + constexpr absl::Duration kHoldLockTime = absl::Seconds(2); + + const DisableSave ds; // Timing-related. + + // We do this in another thread so we can determine if it was actually + // blocked by timing the amount of time it took for the syscall to complete. + ScopedThread t([&] { + MonotonicTimer timer; + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + // This exclusive lock should block because someone is already holding an + // exclusive lock. + timer.Start(); + ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds()); + + // We should be blocked, we will expect to be blocked for more than 1.0s. + ASSERT_GT(timer.Duration(), absl::Seconds(1)); + + // We can release our exclusive lock. + ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); + }); + + // Sleep before unlocking. + absl::SleepFor(kHoldLockTime); + + // Release the exclusive lock allowing the blocked thread to proceed. + // We don't save to avoid wild discrepencies in timing. + EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fork.cc b/test/syscalls/linux/fork.cc new file mode 100644 index 000000000..1bff5e50f --- /dev/null +++ b/test/syscalls/linux/fork.cc @@ -0,0 +1,413 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/logging.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +using ::testing::Ge; + +class ForkTest : public ::testing::Test { + protected: + // SetUp creates a populated, open file. + void SetUp() override { + // Make a shared mapping. + shared_ = reinterpret_cast(mmap(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0)); + ASSERT_NE(reinterpret_cast(shared_), MAP_FAILED); + + // Make a private mapping. + private_ = + reinterpret_cast(mmap(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + ASSERT_NE(reinterpret_cast(private_), MAP_FAILED); + + // Make a pipe. + ASSERT_THAT(pipe(pipes_), SyscallSucceeds()); + } + + // TearDown frees associated resources. + void TearDown() override { + EXPECT_THAT(munmap(shared_, kPageSize), SyscallSucceeds()); + EXPECT_THAT(munmap(private_, kPageSize), SyscallSucceeds()); + EXPECT_THAT(close(pipes_[0]), SyscallSucceeds()); + EXPECT_THAT(close(pipes_[1]), SyscallSucceeds()); + } + + // Fork executes a clone system call. + pid_t Fork() { + pid_t pid = fork(); + MaybeSave(); + TEST_PCHECK_MSG(pid >= 0, "fork failed"); + return pid; + } + + // Wait waits for the given pid and returns the exit status. If the child was + // killed by a signal or an error occurs, then 256+signal is returned. + int Wait(pid_t pid) { + int status; + while (true) { + int rval = wait4(pid, &status, 0, NULL); + if (rval < 0) { + return rval; + } + if (rval != pid) { + continue; + } + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + if (WIFSIGNALED(status)) { + return 256 + WTERMSIG(status); + } + } + } + + // Exit exits the proccess. + void Exit(int code) { + _exit(code); + + // Should never reach here. Since the exit above failed, we really don't + // have much in the way of options to indicate failure. So we just try to + // log an assertion failure to the logs. The parent process will likely + // fail anyways if exit is not working. + TEST_CHECK_MSG(false, "_exit returned"); + } + + // ReadByte reads a byte from the shared pipe. + char ReadByte() { + char val = -1; + TEST_PCHECK(ReadFd(pipes_[0], &val, 1) == 1); + MaybeSave(); + return val; + } + + // WriteByte writes a byte from the shared pipe. + void WriteByte(char val) { + TEST_PCHECK(WriteFd(pipes_[1], &val, 1) == 1); + MaybeSave(); + } + + // Shared pipe. + int pipes_[2]; + + // Shared mapping (one page). + char* shared_; + + // Private mapping (one page). + char* private_; +}; + +TEST_F(ForkTest, Simple) { + pid_t child = Fork(); + if (child == 0) { + Exit(0); + } + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +TEST_F(ForkTest, ExitCode) { + pid_t child = Fork(); + if (child == 0) { + Exit(123); + } + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(123)); + child = Fork(); + if (child == 0) { + Exit(1); + } + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(1)); +} + +TEST_F(ForkTest, Multi) { + pid_t child1 = Fork(); + if (child1 == 0) { + Exit(0); + } + pid_t child2 = Fork(); + if (child2 == 0) { + Exit(1); + } + EXPECT_THAT(Wait(child1), SyscallSucceedsWithValue(0)); + EXPECT_THAT(Wait(child2), SyscallSucceedsWithValue(1)); +} + +TEST_F(ForkTest, Pipe) { + pid_t child = Fork(); + if (child == 0) { + WriteByte(1); + Exit(0); + } + EXPECT_EQ(ReadByte(), 1); + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +TEST_F(ForkTest, SharedMapping) { + pid_t child = Fork(); + if (child == 0) { + // Wait for the parent. + ReadByte(); + if (shared_[0] == 1) { + Exit(0); + } + // Failed. + Exit(1); + } + // Change the mapping. + ASSERT_EQ(shared_[0], 0); + shared_[0] = 1; + // Unblock the child. + WriteByte(0); + // Did it work? + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +TEST_F(ForkTest, PrivateMapping) { + pid_t child = Fork(); + if (child == 0) { + // Wait for the parent. + ReadByte(); + if (private_[0] == 0) { + Exit(0); + } + // Failed. + Exit(1); + } + // Change the mapping. + ASSERT_EQ(private_[0], 0); + private_[0] = 1; + // Unblock the child. + WriteByte(0); + // Did it work? + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +// Test that cpuid works after a fork. +TEST_F(ForkTest, Cpuid) { + pid_t child = Fork(); + + // We should be able to determine the CPU vendor. + ASSERT_NE(GetCPUVendor(), CPUVendor::kUnknownVendor); + + if (child == 0) { + Exit(0); + } + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +TEST_F(ForkTest, Mmap) { + pid_t child = Fork(); + + if (child == 0) { + void* addr = + mmap(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + MaybeSave(); + Exit(addr == MAP_FAILED); + } + + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +static volatile int alarmed = 0; + +void AlarmHandler(int sig, siginfo_t* info, void* context) { alarmed = 1; } + +TEST_F(ForkTest, Alarm) { + // Setup an alarm handler. + struct sigaction sa; + sa.sa_sigaction = AlarmHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + EXPECT_THAT(sigaction(SIGALRM, &sa, nullptr), SyscallSucceeds()); + + pid_t child = Fork(); + + if (child == 0) { + alarm(1); + sleep(3); + if (!alarmed) { + Exit(1); + } + Exit(0); + } + + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); + EXPECT_EQ(0, alarmed); +} + +// Child cannot affect parent private memory. +TEST_F(ForkTest, PrivateMemory) { + std::atomic local(0); + + pid_t child1 = Fork(); + if (child1 == 0) { + local++; + + pid_t child2 = Fork(); + if (child2 == 0) { + local++; + + TEST_CHECK(local.load() == 2); + + Exit(0); + } + + TEST_PCHECK(Wait(child2) == 0); + TEST_CHECK(local.load() == 1); + Exit(0); + } + + EXPECT_THAT(Wait(child1), SyscallSucceedsWithValue(0)); + EXPECT_EQ(0, local.load()); +} + +// Kernel-accessed buffers should remain coherent across COW. +TEST_F(ForkTest, COWSegment) { + constexpr int kBufSize = 1024; + char* read_buf = private_; + char* touch = private_ + kPageSize / 2; + + std::string contents(kBufSize, 'a'); + + ScopedThread t([&] { + // Wait to be sure the parent is blocked in read. + absl::SleepFor(absl::Seconds(3)); + + // Fork to mark private pages for COW. + // + // Use fork directly rather than the Fork wrapper to skip the multi-threaded + // check, and limit the child to async-signal-safe functions: + // + // "After a fork() in a multithreaded program, the child can safely call + // only async-signal-safe functions (see signal(7)) until such time as it + // calls execve(2)." + // + // Skip ASSERT in the child, as it isn't async-signal-safe. + pid_t child = fork(); + if (child == 0) { + // Wait to be sure parent touched memory. + sleep(3); + Exit(0); + } + + // Check success only in the parent. + ASSERT_THAT(child, SyscallSucceedsWithValue(Ge(0))); + + // Trigger COW on private page. + *touch = 42; + + // Write to pipe. Parent should still be able to read this. + EXPECT_THAT(WriteFd(pipes_[1], contents.c_str(), kBufSize), + SyscallSucceedsWithValue(kBufSize)); + + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); + }); + + EXPECT_THAT(ReadFd(pipes_[0], read_buf, kBufSize), + SyscallSucceedsWithValue(kBufSize)); + EXPECT_STREQ(contents.c_str(), read_buf); +} + +TEST_F(ForkTest, SigAltStack) { + std::vector stack_mem(SIGSTKSZ); + stack_t stack = {}; + stack.ss_size = SIGSTKSZ; + stack.ss_sp = stack_mem.data(); + ASSERT_THAT(sigaltstack(&stack, nullptr), SyscallSucceeds()); + + pid_t child = Fork(); + + if (child == 0) { + stack_t oss = {}; + TEST_PCHECK(sigaltstack(nullptr, &oss) == 0); + MaybeSave(); + + TEST_CHECK((oss.ss_flags & SS_DISABLE) == 0); + TEST_CHECK(oss.ss_size == SIGSTKSZ); + TEST_CHECK(oss.ss_sp == stack.ss_sp); + + Exit(0); + } + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +TEST_F(ForkTest, Affinity) { + // Make a non-default cpumask. + cpu_set_t parent_mask; + EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &parent_mask), + SyscallSucceeds()); + // Knock out the lowest bit. + for (unsigned int n = 0; n < CPU_SETSIZE; n++) { + if (CPU_ISSET(n, &parent_mask)) { + CPU_CLR(n, &parent_mask); + break; + } + } + EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &parent_mask), + SyscallSucceeds()); + + pid_t child = Fork(); + if (child == 0) { + cpu_set_t child_mask; + + int ret = sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &child_mask); + MaybeSave(); + if (ret < 0) { + Exit(-ret); + } + + TEST_CHECK(CPU_EQUAL(&child_mask, &parent_mask)); + + Exit(0); + } + + EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); +} + +#ifdef __x86_64__ +// Clone with CLONE_SETTLS and a non-canonical TLS address is rejected. +TEST(CloneTest, NonCanonicalTLS) { + constexpr uintptr_t kNonCanonical = 1ull << 48; + + // We need a valid address for the stack pointer. We'll never actually execute + // on this. + char stack; + + EXPECT_THAT(syscall(__NR_clone, SIGCHLD | CLONE_SETTLS, &stack, nullptr, + nullptr, kNonCanonical), + SyscallFailsWithErrno(EPERM)); +} +#endif + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fpsig_fork.cc b/test/syscalls/linux/fpsig_fork.cc new file mode 100644 index 000000000..e8f1dfa8a --- /dev/null +++ b/test/syscalls/linux/fpsig_fork.cc @@ -0,0 +1,105 @@ +// Copyright 2018 Google LLC +// +// 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. + +// This test verifies that fork(2) in a signal handler will correctly +// restore floating point state after the signal handler returns in both +// the child and parent. +#include + +#include "gtest/gtest.h" +#include "test/util/logging.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +#define GET_XMM(__var, __xmm) \ + asm volatile("movq %%" #__xmm ", %0" : "=r"(__var)) +#define SET_XMM(__var, __xmm) asm volatile("movq %0, %%" #__xmm : : "r"(__var)) + +int parent, child; + +void sigusr1(int s, siginfo_t* siginfo, void* _uc) { + // Fork and clobber %xmm0. The fpstate should be restored by sigreturn(2) + // in both parent and child. + child = fork(); + TEST_CHECK_MSG(child >= 0, "fork failed"); + + uint64_t val = SIGUSR1; + SET_XMM(val, xmm0); +} + +TEST(FPSigTest, Fork) { + parent = getpid(); + pid_t parent_tid = gettid(); + + struct sigaction sa = {}; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = sigusr1; + ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); + + // The amd64 ABI specifies that the XMM register set is caller-saved. This + // implies that if there is any function call between SET_XMM and GET_XMM the + // compiler might save/restore xmm0 implicitly. This defeats the entire + // purpose of the test which is to verify that fpstate is restored by + // sigreturn(2). + // + // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented + // in inline assembly below. + // + // If the OS is broken and registers are clobbered by the child, using tgkill + // to signal the current thread increases the likelihood that this thread will + // be the one clobbered. + + uint64_t expected = 0xdeadbeeffacefeed; + SET_XMM(expected, xmm0); + + asm volatile( + "movl %[killnr], %%eax;" + "movl %[parent], %%edi;" + "movl %[tid], %%esi;" + "movl %[sig], %%edx;" + "syscall;" + : + : [killnr] "i"(__NR_tgkill), [parent] "rm"(parent), + [tid] "rm"(parent_tid), [sig] "i"(SIGUSR1) + : "rax", "rdi", "rsi", "rdx", + // Clobbered by syscall. + "rcx", "r11"); + + uint64_t got; + GET_XMM(got, xmm0); + + if (getpid() == parent) { // Parent. + int status; + ASSERT_THAT(waitpid(child, &status, 0), SyscallSucceedsWithValue(child)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); + } + + // TEST_CHECK_MSG since this may run in the child. + TEST_CHECK_MSG(expected == got, "Bad xmm0 value"); + + if (getpid() != parent) { // Child. + _exit(0); + } +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fpsig_nested.cc b/test/syscalls/linux/fpsig_nested.cc new file mode 100644 index 000000000..2fa40b42d --- /dev/null +++ b/test/syscalls/linux/fpsig_nested.cc @@ -0,0 +1,134 @@ +// Copyright 2018 Google LLC +// +// 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. + +// This program verifies that application floating point state is restored +// correctly after a signal handler returns. It also verifies that this works +// with nested signals. +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +#define GET_XMM(__var, __xmm) \ + asm volatile("movq %%" #__xmm ", %0" : "=r"(__var)) +#define SET_XMM(__var, __xmm) asm volatile("movq %0, %%" #__xmm : : "r"(__var)) + +int pid; +int tid; + +volatile uint64_t entryxmm[2] = {~0UL, ~0UL}; +volatile uint64_t exitxmm[2]; + +void sigusr2(int s, siginfo_t* siginfo, void* _uc) { + uint64_t val = SIGUSR2; + + // Record the value of %xmm0 on entry and then clobber it. + GET_XMM(entryxmm[1], xmm0); + SET_XMM(val, xmm0); + GET_XMM(exitxmm[1], xmm0); +} + +void sigusr1(int s, siginfo_t* siginfo, void* _uc) { + uint64_t val = SIGUSR1; + + // Record the value of %xmm0 on entry and then clobber it. + GET_XMM(entryxmm[0], xmm0); + SET_XMM(val, xmm0); + + // Send a SIGUSR2 to ourself. The signal mask is configured such that + // the SIGUSR2 handler will run before this handler returns. + asm volatile( + "movl %[killnr], %%eax;" + "movl %[pid], %%edi;" + "movl %[tid], %%esi;" + "movl %[sig], %%edx;" + "syscall;" + : + : [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid), + [sig] "i"(SIGUSR2) + : "rax", "rdi", "rsi", "rdx", + // Clobbered by syscall. + "rcx", "r11"); + + // Record value of %xmm0 again to verify that the nested signal handler + // does not clobber it. + GET_XMM(exitxmm[0], xmm0); +} + +TEST(FPSigTest, NestedSignals) { + pid = getpid(); + tid = gettid(); + + struct sigaction sa = {}; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = sigusr1; + ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); + + sa.sa_sigaction = sigusr2; + ASSERT_THAT(sigaction(SIGUSR2, &sa, nullptr), SyscallSucceeds()); + + // The amd64 ABI specifies that the XMM register set is caller-saved. This + // implies that if there is any function call between SET_XMM and GET_XMM the + // compiler might save/restore xmm0 implicitly. This defeats the entire + // purpose of the test which is to verify that fpstate is restored by + // sigreturn(2). + // + // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented + // in inline assembly below. + // + // If the OS is broken and registers are clobbered by the signal, using tgkill + // to signal the current thread ensures that this is the clobbered thread. + + uint64_t expected = 0xdeadbeeffacefeed; + SET_XMM(expected, xmm0); + + asm volatile( + "movl %[killnr], %%eax;" + "movl %[pid], %%edi;" + "movl %[tid], %%esi;" + "movl %[sig], %%edx;" + "syscall;" + : + : [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid), + [sig] "i"(SIGUSR1) + : "rax", "rdi", "rsi", "rdx", + // Clobbered by syscall. + "rcx", "r11"); + + uint64_t got; + GET_XMM(got, xmm0); + + // + // The checks below verifies the following: + // - signal handlers must called with a clean fpu state. + // - sigreturn(2) must restore fpstate of the interrupted context. + // + EXPECT_EQ(expected, got); + EXPECT_EQ(entryxmm[0], 0); + EXPECT_EQ(entryxmm[1], 0); + EXPECT_EQ(exitxmm[0], SIGUSR1); + EXPECT_EQ(exitxmm[1], SIGUSR2); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/fsync.cc b/test/syscalls/linux/fsync.cc new file mode 100644 index 000000000..536a73bf1 --- /dev/null +++ b/test/syscalls/linux/fsync.cc @@ -0,0 +1,55 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(FsyncTest, TempFileSucceeds) { + std::string path = NewTempAbsPath(); + int fd; + EXPECT_THAT(fd = open(path.c_str(), O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + const std::string data = "some data to sync"; + EXPECT_THAT(write(fd, data.c_str(), data.size()), + SyscallSucceedsWithValue(data.size())); + EXPECT_THAT(fsync(fd), SyscallSucceeds()); + ASSERT_THAT(close(fd), SyscallSucceeds()); + ASSERT_THAT(unlink(path.c_str()), SyscallSucceeds()); +} + +TEST(FsyncTest, CannotFsyncOnUnopenedFd) { + int fd; + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + ASSERT_THAT(fd = open(f.path().c_str(), O_RDONLY), SyscallSucceeds()); + ASSERT_THAT(close(fd), SyscallSucceeds()); + + // fd is now invalid. + EXPECT_THAT(fsync(fd), SyscallFailsWithErrno(EBADF)); +} +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/futex.cc b/test/syscalls/linux/futex.cc new file mode 100644 index 000000000..6fa284013 --- /dev/null +++ b/test/syscalls/linux/futex.cc @@ -0,0 +1,595 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/memory_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Amount of time we wait for threads doing futex_wait to start running before +// doing futex_wake. +constexpr auto kWaiterStartupDelay = absl::Seconds(3); + +// Default timeout for waiters in tests where we expect a futex_wake to be +// ineffective. +constexpr auto kIneffectiveWakeTimeout = absl::Seconds(6); + +static_assert(kWaiterStartupDelay < kIneffectiveWakeTimeout, + "futex_wait will time out before futex_wake is called"); + +int futex_wait(bool priv, std::atomic* uaddr, int val, + absl::Duration timeout = absl::InfiniteDuration()) { + int op = FUTEX_WAIT; + if (priv) { + op |= FUTEX_PRIVATE_FLAG; + } + + if (timeout == absl::InfiniteDuration()) { + return RetryEINTR(syscall)(SYS_futex, uaddr, op, val, nullptr); + } + + // FUTEX_WAIT doesn't adjust the timeout if it returns EINTR, so we have to do + // so. + while (true) { + auto const timeout_ts = absl::ToTimespec(timeout); + MonotonicTimer timer; + timer.Start(); + int const ret = syscall(SYS_futex, uaddr, op, val, &timeout_ts); + if (ret != -1 || errno != EINTR) { + return ret; + } + timeout = std::max(timeout - timer.Duration(), absl::ZeroDuration()); + } +} + +int futex_wait_bitset(bool priv, std::atomic* uaddr, int val, int bitset, + absl::Time deadline = absl::InfiniteFuture()) { + int op = FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME; + if (priv) { + op |= FUTEX_PRIVATE_FLAG; + } + + auto const deadline_ts = absl::ToTimespec(deadline); + return RetryEINTR(syscall)( + SYS_futex, uaddr, op, val, + deadline == absl::InfiniteFuture() ? nullptr : &deadline_ts, nullptr, + bitset); +} + +int futex_wake(bool priv, std::atomic* uaddr, int count) { + int op = FUTEX_WAKE; + if (priv) { + op |= FUTEX_PRIVATE_FLAG; + } + return syscall(SYS_futex, uaddr, op, count); +} + +int futex_wake_bitset(bool priv, std::atomic* uaddr, int count, + int bitset) { + int op = FUTEX_WAKE_BITSET; + if (priv) { + op |= FUTEX_PRIVATE_FLAG; + } + return syscall(SYS_futex, uaddr, op, count, nullptr, nullptr, bitset); +} + +int futex_wake_op(bool priv, std::atomic* uaddr1, std::atomic* uaddr2, + int nwake1, int nwake2, uint32_t sub_op) { + int op = FUTEX_WAKE_OP; + if (priv) { + op |= FUTEX_PRIVATE_FLAG; + } + return syscall(SYS_futex, uaddr1, op, nwake1, nwake2, uaddr2, sub_op); +} + +// Fixture for futex tests parameterized by whether to use private or shared +// futexes. +class PrivateAndSharedFutexTest : public ::testing::TestWithParam { + protected: + bool IsPrivate() const { return GetParam(); } + int PrivateFlag() const { return IsPrivate() ? FUTEX_PRIVATE_FLAG : 0; } +}; + +// FUTEX_WAIT with 0 timeout does not block. +TEST_P(PrivateAndSharedFutexTest, Wait_ZeroTimeout) { + struct timespec timeout = {}; + + // Don't use the futex_wait helper because it adjusts timeout. + int a = 1; + EXPECT_THAT(syscall(SYS_futex, &a, FUTEX_WAIT | PrivateFlag(), a, &timeout), + SyscallFailsWithErrno(ETIMEDOUT)); +} + +TEST_P(PrivateAndSharedFutexTest, Wait_Timeout) { + std::atomic a = ATOMIC_VAR_INIT(1); + + MonotonicTimer timer; + timer.Start(); + constexpr absl::Duration kTimeout = absl::Seconds(1); + EXPECT_THAT(futex_wait(IsPrivate(), &a, a, kTimeout), + SyscallFailsWithErrno(ETIMEDOUT)); + EXPECT_GE(timer.Duration(), kTimeout); +} + +TEST_P(PrivateAndSharedFutexTest, Wait_BitsetTimeout) { + std::atomic a = ATOMIC_VAR_INIT(1); + + MonotonicTimer timer; + timer.Start(); + constexpr absl::Duration kTimeout = absl::Seconds(1); + EXPECT_THAT( + futex_wait_bitset(IsPrivate(), &a, a, 0xffffffff, absl::Now() + kTimeout), + SyscallFailsWithErrno(ETIMEDOUT)); + EXPECT_GE(timer.Duration(), kTimeout); +} + +TEST_P(PrivateAndSharedFutexTest, WaitBitset_NegativeTimeout) { + std::atomic a = ATOMIC_VAR_INIT(1); + + MonotonicTimer timer; + timer.Start(); + EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, a, 0xffffffff, + absl::Now() - absl::Seconds(1)), + SyscallFailsWithErrno(ETIMEDOUT)); +} + +TEST_P(PrivateAndSharedFutexTest, Wait_WrongVal) { + std::atomic a = ATOMIC_VAR_INIT(1); + EXPECT_THAT(futex_wait(IsPrivate(), &a, a + 1), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(PrivateAndSharedFutexTest, Wait_ZeroBitset) { + std::atomic a = ATOMIC_VAR_INIT(1); + EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, a, 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(PrivateAndSharedFutexTest, Wake1_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + // Prevent save/restore from interrupting futex_wait, which will cause it to + // return EAGAIN instead of the expected result if futex_wait is restarted + // after we change the value of a below. + DisableSave ds; + ScopedThread thread([&] { + EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), + SyscallSucceedsWithValue(0)); + }); + absl::SleepFor(kWaiterStartupDelay); + + // Change a so that if futex_wake happens before futex_wait, the latter + // returns EAGAIN instead of hanging the test. + a.fetch_add(1); + EXPECT_THAT(futex_wake(IsPrivate(), &a, 1), SyscallSucceedsWithValue(1)); +} + +TEST_P(PrivateAndSharedFutexTest, WakeAll_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + DisableSave ds; + constexpr int kThreads = 5; + std::vector> threads; + threads.reserve(kThreads); + for (int i = 0; i < kThreads; i++) { + threads.push_back(absl::make_unique([&] { + EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), + SyscallSucceeds()); + })); + } + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + EXPECT_THAT(futex_wake(IsPrivate(), &a, kThreads), + SyscallSucceedsWithValue(kThreads)); +} + +TEST_P(PrivateAndSharedFutexTest, WakeSome_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + DisableSave ds; + constexpr int kThreads = 5; + constexpr int kWokenThreads = 3; + static_assert(kWokenThreads < kThreads, + "can't wake more threads than are created"); + std::vector> threads; + threads.reserve(kThreads); + std::vector rets; + rets.reserve(kThreads); + std::vector errs; + errs.reserve(kThreads); + for (int i = 0; i < kThreads; i++) { + rets.push_back(-1); + errs.push_back(0); + } + for (int i = 0; i < kThreads; i++) { + threads.push_back(absl::make_unique([&, i] { + rets[i] = + futex_wait(IsPrivate(), &a, kInitialValue, kIneffectiveWakeTimeout); + errs[i] = errno; + })); + } + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + EXPECT_THAT(futex_wake(IsPrivate(), &a, kWokenThreads), + SyscallSucceedsWithValue(kWokenThreads)); + + int woken = 0; + int timedout = 0; + for (int i = 0; i < kThreads; i++) { + threads[i]->Join(); + if (rets[i] == 0) { + woken++; + } else if (errs[i] == ETIMEDOUT) { + timedout++; + } else { + ADD_FAILURE() << " thread " << i << ": returned " << rets[i] << ", errno " + << errs[i]; + } + } + EXPECT_EQ(woken, kWokenThreads); + EXPECT_EQ(timedout, kThreads - kWokenThreads); +} + +TEST_P(PrivateAndSharedFutexTest, WaitBitset_Wake_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + DisableSave ds; + ScopedThread thread([&] { + EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, kInitialValue, 0b01001000), + SyscallSucceeds()); + }); + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + EXPECT_THAT(futex_wake(IsPrivate(), &a, 1), SyscallSucceedsWithValue(1)); +} + +TEST_P(PrivateAndSharedFutexTest, Wait_WakeBitset_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + DisableSave ds; + ScopedThread thread([&] { + EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), SyscallSucceeds()); + }); + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + EXPECT_THAT(futex_wake_bitset(IsPrivate(), &a, 1, 0b01001000), + SyscallSucceedsWithValue(1)); +} + +TEST_P(PrivateAndSharedFutexTest, WaitBitset_WakeBitsetMatch_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + constexpr int kBitset = 0b01001000; + + DisableSave ds; + ScopedThread thread([&] { + EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, kInitialValue, kBitset), + SyscallSucceeds()); + }); + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + EXPECT_THAT(futex_wake_bitset(IsPrivate(), &a, 1, kBitset), + SyscallSucceedsWithValue(1)); +} + +TEST_P(PrivateAndSharedFutexTest, WaitBitset_WakeBitsetNoMatch_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + constexpr int kWaitBitset = 0b01000001; + constexpr int kWakeBitset = 0b00101000; + static_assert((kWaitBitset & kWakeBitset) == 0, + "futex_wake_bitset will wake waiter"); + + DisableSave ds; + ScopedThread thread([&] { + EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, kInitialValue, kWaitBitset, + absl::Now() + kIneffectiveWakeTimeout), + SyscallFailsWithErrno(ETIMEDOUT)); + }); + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + EXPECT_THAT(futex_wake_bitset(IsPrivate(), &a, 1, kWakeBitset), + SyscallSucceedsWithValue(0)); +} + +TEST_P(PrivateAndSharedFutexTest, WakeOpCondSuccess_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + std::atomic b = ATOMIC_VAR_INIT(kInitialValue); + + DisableSave ds; + ScopedThread thread_a([&] { + EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), SyscallSucceeds()); + }); + ScopedThread thread_b([&] { + EXPECT_THAT(futex_wait(IsPrivate(), &b, kInitialValue), SyscallSucceeds()); + }); + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + b.fetch_add(1); + // This futex_wake_op should: + // - Wake 1 waiter on a unconditionally. + // - Wake 1 waiter on b if b == kInitialValue + 1, which it is. + // - Do "b += 1". + EXPECT_THAT(futex_wake_op(IsPrivate(), &a, &b, 1, 1, + FUTEX_OP(FUTEX_OP_ADD, 1, FUTEX_OP_CMP_EQ, + (kInitialValue + 1))), + SyscallSucceedsWithValue(2)); + EXPECT_EQ(b, kInitialValue + 2); +} + +TEST_P(PrivateAndSharedFutexTest, WakeOpCondFailure_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + std::atomic b = ATOMIC_VAR_INIT(kInitialValue); + + DisableSave ds; + ScopedThread thread_a([&] { + EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), SyscallSucceeds()); + }); + ScopedThread thread_b([&] { + EXPECT_THAT( + futex_wait(IsPrivate(), &b, kInitialValue, kIneffectiveWakeTimeout), + SyscallFailsWithErrno(ETIMEDOUT)); + }); + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + b.fetch_add(1); + // This futex_wake_op should: + // - Wake 1 waiter on a unconditionally. + // - Wake 1 waiter on b if b == kInitialValue - 1, which it isn't. + // - Do "b += 1". + EXPECT_THAT(futex_wake_op(IsPrivate(), &a, &b, 1, 1, + FUTEX_OP(FUTEX_OP_ADD, 1, FUTEX_OP_CMP_EQ, + (kInitialValue - 1))), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(b, kInitialValue + 2); +} + +TEST_P(PrivateAndSharedFutexTest, NoWakeInterprocessPrivateAnon_NoRandomSave) { + auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + auto const ptr = static_cast*>(mapping.ptr()); + constexpr int kInitialValue = 1; + ptr->store(kInitialValue); + + DisableSave ds; + pid_t const child_pid = fork(); + if (child_pid == 0) { + TEST_PCHECK(futex_wait(IsPrivate(), ptr, kInitialValue, + kIneffectiveWakeTimeout) == -1 && + errno == ETIMEDOUT); + _exit(0); + } + ASSERT_THAT(child_pid, SyscallSucceeds()); + absl::SleepFor(kWaiterStartupDelay); + + EXPECT_THAT(futex_wake(IsPrivate(), ptr, 1), SyscallSucceedsWithValue(0)); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +TEST_P(PrivateAndSharedFutexTest, WakeAfterCOWBreak_NoRandomSave) { + // Use a futex on a non-stack mapping so we can be sure that the child process + // below isn't the one that breaks copy-on-write. + auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + auto const ptr = static_cast*>(mapping.ptr()); + constexpr int kInitialValue = 1; + ptr->store(kInitialValue); + + DisableSave ds; + ScopedThread thread([&] { + EXPECT_THAT(futex_wait(IsPrivate(), ptr, kInitialValue), SyscallSucceeds()); + }); + absl::SleepFor(kWaiterStartupDelay); + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // Wait to be killed by the parent. + while (true) pause(); + } + ASSERT_THAT(child_pid, SyscallSucceeds()); + auto cleanup_child = Cleanup([&] { + EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + << " status " << status; + }); + + // In addition to preventing a late futex_wait from sleeping, this breaks + // copy-on-write on the mapped page. + ptr->fetch_add(1); + EXPECT_THAT(futex_wake(IsPrivate(), ptr, 1), SyscallSucceedsWithValue(1)); +} + +TEST_P(PrivateAndSharedFutexTest, WakeWrongKind_NoRandomSave) { + constexpr int kInitialValue = 1; + std::atomic a = ATOMIC_VAR_INIT(kInitialValue); + + DisableSave ds; + ScopedThread thread([&] { + EXPECT_THAT( + futex_wait(IsPrivate(), &a, kInitialValue, kIneffectiveWakeTimeout), + SyscallFailsWithErrno(ETIMEDOUT)); + }); + absl::SleepFor(kWaiterStartupDelay); + + a.fetch_add(1); + // The value of priv passed to futex_wake is the opposite of that passed to + // the futex_waiter; we expect this not to wake the waiter. + EXPECT_THAT(futex_wake(!IsPrivate(), &a, 1), SyscallSucceedsWithValue(0)); +} + +INSTANTIATE_TEST_CASE_P(SharedPrivate, PrivateAndSharedFutexTest, + ::testing::Bool()); + +// Passing null as the address only works for private futexes. + +TEST(PrivateFutexTest, WakeOp0Set) { + std::atomic a = ATOMIC_VAR_INIT(1); + + int futex_op = FUTEX_OP(FUTEX_OP_SET, 2, 0, 0); + EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(a, 2); +} + +TEST(PrivateFutexTest, WakeOp0Add) { + std::atomic a = ATOMIC_VAR_INIT(1); + int futex_op = FUTEX_OP(FUTEX_OP_ADD, 1, 0, 0); + EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(a, 2); +} + +TEST(PrivateFutexTest, WakeOp0Or) { + std::atomic a = ATOMIC_VAR_INIT(0b01); + int futex_op = FUTEX_OP(FUTEX_OP_OR, 0b10, 0, 0); + EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(a, 0b11); +} + +TEST(PrivateFutexTest, WakeOp0Andn) { + std::atomic a = ATOMIC_VAR_INIT(0b11); + int futex_op = FUTEX_OP(FUTEX_OP_ANDN, 0b10, 0, 0); + EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(a, 0b01); +} + +TEST(PrivateFutexTest, WakeOp0Xor) { + std::atomic a = ATOMIC_VAR_INIT(0b1010); + int futex_op = FUTEX_OP(FUTEX_OP_XOR, 0b1100, 0, 0); + EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(a, 0b0110); +} + +TEST(SharedFutexTest, WakeInterprocessSharedAnon_NoRandomSave) { + auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED)); + auto const ptr = static_cast*>(mapping.ptr()); + constexpr int kInitialValue = 1; + ptr->store(kInitialValue); + + DisableSave ds; + pid_t const child_pid = fork(); + if (child_pid == 0) { + TEST_PCHECK(futex_wait(false, ptr, kInitialValue) == 0); + _exit(0); + } + ASSERT_THAT(child_pid, SyscallSucceeds()); + auto kill_child = Cleanup( + [&] { EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); }); + absl::SleepFor(kWaiterStartupDelay); + + ptr->fetch_add(1); + // This is an ASSERT so that if it fails, we immediately abort the test (and + // kill the subprocess). + ASSERT_THAT(futex_wake(false, ptr, 1), SyscallSucceedsWithValue(1)); + + kill_child.Release(); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +TEST(SharedFutexTest, WakeInterprocessFile_NoRandomSave) { + auto const file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + ASSERT_THAT(truncate(file.path().c_str(), kPageSize), SyscallSucceeds()); + auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); + auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(Mmap( + nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); + auto const ptr = static_cast*>(mapping.ptr()); + constexpr int kInitialValue = 1; + ptr->store(kInitialValue); + + DisableSave ds; + pid_t const child_pid = fork(); + if (child_pid == 0) { + TEST_PCHECK(futex_wait(false, ptr, kInitialValue) == 0); + _exit(0); + } + ASSERT_THAT(child_pid, SyscallSucceeds()); + auto kill_child = Cleanup( + [&] { EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); }); + absl::SleepFor(kWaiterStartupDelay); + + ptr->fetch_add(1); + // This is an ASSERT so that if it fails, we immediately abort the test (and + // kill the subprocess). + ASSERT_THAT(futex_wake(false, ptr, 1), SyscallSucceedsWithValue(1)); + + kill_child.Release(); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/getcpu.cc b/test/syscalls/linux/getcpu.cc new file mode 100644 index 000000000..3a52b25fa --- /dev/null +++ b/test/syscalls/linux/getcpu.cc @@ -0,0 +1,40 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(GetcpuTest, IsValidCpuStress) { + const int num_cpus = NumCPUs(); + absl::Time deadline = absl::Now() + absl::Seconds(10); + while (absl::Now() < deadline) { + int cpu; + ASSERT_THAT(cpu = sched_getcpu(), SyscallSucceeds()); + ASSERT_LT(cpu, num_cpus); + } +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/getdents.cc b/test/syscalls/linux/getdents.cc new file mode 100644 index 000000000..5db580aa0 --- /dev/null +++ b/test/syscalls/linux/getdents.cc @@ -0,0 +1,485 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +using ::testing::IsEmpty; +using ::testing::IsSupersetOf; +using ::testing::Not; +using ::testing::NotNull; + +namespace gvisor { +namespace testing { + +namespace { + +// New Linux dirent format. +struct linux_dirent64 { + uint64_t d_ino; // Inode number + int64_t d_off; // Offset to next linux_dirent64 + unsigned short d_reclen; // NOLINT, Length of this linux_dirent64 + unsigned char d_type; // NOLINT, File type + char d_name[0]; // Filename (null-terminated) +}; + +// Old Linux dirent format. +struct linux_dirent { + unsigned long d_ino; // NOLINT + unsigned long d_off; // NOLINT + unsigned short d_reclen; // NOLINT + char d_name[0]; +}; + +// Wraps a buffer to provide a set of dirents. +// T is the underlying dirent type. +template +class DirentBuffer { + public: + // DirentBuffer manages the buffer. + explicit DirentBuffer(size_t size) + : managed_(true), actual_size_(size), reported_size_(size) { + data_ = new char[actual_size_]; + } + + // The buffer is managed externally. + DirentBuffer(char* data, size_t actual_size, size_t reported_size) + : managed_(false), + data_(data), + actual_size_(actual_size), + reported_size_(reported_size) {} + + ~DirentBuffer() { + if (managed_) { + delete[] data_; + } + } + + T* Data() { return reinterpret_cast(data_); } + + T* Start(size_t read) { + read_ = read; + if (read_) { + return Data(); + } else { + return nullptr; + } + } + + T* Current() { return reinterpret_cast(&data_[off_]); } + + T* Next() { + size_t new_off = off_ + Current()->d_reclen; + if (new_off >= read_ || new_off >= actual_size_) { + return nullptr; + } + + off_ = new_off; + return Current(); + } + + size_t Size() { return reported_size_; } + + void Reset() { + off_ = 0; + read_ = 0; + memset(data_, 0, actual_size_); + } + + private: + bool managed_; + char* data_; + size_t actual_size_; + size_t reported_size_; + + size_t off_ = 0; + + size_t read_ = 0; +}; + +// Test for getdents/getdents64. +// T is the Linux dirent type. +template +class GetdentsTest : public ::testing::Test { + public: + using LinuxDirentType = T; + using DirentBufferType = DirentBuffer; + + protected: + void SetUp() override { + dir_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + fd_ = ASSERT_NO_ERRNO_AND_VALUE(Open(dir_.path(), O_RDONLY | O_DIRECTORY)); + } + + // Must be overridden with explicit specialization. See below. + int SyscallNum(); + + int Getdents(LinuxDirentType* dirp, unsigned int count) { + return RetryEINTR(syscall)(SyscallNum(), fd_.get(), dirp, count); + } + + // Fill directory with num files, named by number starting at 0. + void FillDirectory(size_t num) { + for (size_t i = 0; i < num; i++) { + auto name = JoinPath(dir_.path(), absl::StrCat(i)); + TEST_CHECK(CreateWithContents(name, "").ok()); + } + } + + // Fill directory with a given list of filenames. + void FillDirectoryWithFiles(const std::vector& filenames) { + for (const auto& filename : filenames) { + auto name = JoinPath(dir_.path(), filename); + TEST_CHECK(CreateWithContents(name, "").ok()); + } + } + + // Seek to the start of the directory. + PosixError SeekStart() { + constexpr off_t kStartOfFile = 0; + off_t offset = lseek(fd_.get(), kStartOfFile, SEEK_SET); + if (offset < 0) { + return PosixError(errno, absl::StrCat("error seeking to ", kStartOfFile)); + } + if (offset != kStartOfFile) { + return PosixError(EINVAL, absl::StrCat("tried to seek to ", kStartOfFile, + " but got ", offset)); + } + return NoError(); + } + + // Call getdents multiple times, reading all dirents and calling f on each. + // f has the type signature PosixError f(T*). + // If f returns a non-OK error, so does ReadDirents. + template + PosixError ReadDirents(DirentBufferType* dirents, F const& f) { + int n; + do { + dirents->Reset(); + + n = Getdents(dirents->Data(), dirents->Size()); + MaybeSave(); + if (n < 0) { + return PosixError(errno, "getdents"); + } + + for (auto d = dirents->Start(n); d; d = dirents->Next()) { + RETURN_IF_ERRNO(f(d)); + } + } while (n > 0); + + return NoError(); + } + + // Call Getdents successively and count all entries. + int ReadAndCountAllEntries(DirentBufferType* dirents) { + int found = 0; + + EXPECT_NO_ERRNO(ReadDirents(dirents, [&](LinuxDirentType* d) { + found++; + return NoError(); + })); + + return found; + } + + private: + TempPath dir_; + FileDescriptor fd_; +}; + +// GUnit TYPED_TEST_CASE does not allow multiple template parameters, so we +// must use explicit template specialization to set the syscall number. +template <> +int GetdentsTest::SyscallNum() { + return SYS_getdents; +} + +template <> +int GetdentsTest::SyscallNum() { + return SYS_getdents64; +} + +// Test both legacy getdents and getdents64. +typedef ::testing::Types + GetdentsTypes; +TYPED_TEST_CASE(GetdentsTest, GetdentsTypes); + +// N.B. TYPED_TESTs require explicitly using this-> to access members of +// GetdentsTest, since we are inside of a derived class template. + +TYPED_TEST(GetdentsTest, VerifyEntries) { + typename TestFixture::DirentBufferType dirents(1024); + + this->FillDirectory(2); + + // Map of all the entries we expect to find. + std::map found; + found["."] = false; + found[".."] = false; + found["0"] = false; + found["1"] = false; + + EXPECT_NO_ERRNO(this->ReadDirents( + &dirents, [&](typename TestFixture::LinuxDirentType* d) { + auto kv = found.find(d->d_name); + EXPECT_NE(kv, found.end()) << "Unexpected file: " << d->d_name; + if (kv != found.end()) { + EXPECT_FALSE(kv->second); + } + found[d->d_name] = true; + return NoError(); + })); + + for (auto& kv : found) { + EXPECT_TRUE(kv.second) << "File not found: " << kv.first; + } +} + +TYPED_TEST(GetdentsTest, VerifyPadding) { + typename TestFixture::DirentBufferType dirents(1024); + + // Create files with names of length 1 through 16. + std::vector files; + std::string filename; + for (int i = 0; i < 16; ++i) { + absl::StrAppend(&filename, "a"); + files.push_back(filename); + } + this->FillDirectoryWithFiles(files); + + // We expect to find all the files, plus '.' and '..'. + const int expect_found = 2 + files.size(); + int found = 0; + + EXPECT_NO_ERRNO(this->ReadDirents( + &dirents, [&](typename TestFixture::LinuxDirentType* d) { + EXPECT_EQ(d->d_reclen % 8, 0) + << "Dirent " << d->d_name + << " had reclen that was not byte aligned: " << d->d_name; + found++; + return NoError(); + })); + + // Make sure we found all the files. + EXPECT_EQ(found, expect_found); +} + +// For a small directory, the provided buffer should be large enough +// for all entries. +TYPED_TEST(GetdentsTest, SmallDir) { + // . and .. should be in an otherwise empty directory. + int expect = 2; + + // Add some actual files. + this->FillDirectory(2); + expect += 2; + + typename TestFixture::DirentBufferType dirents(256); + + EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); +} + +// A directory with lots of files requires calling getdents multiple times. +TYPED_TEST(GetdentsTest, LargeDir) { + // . and .. should be in an otherwise empty directory. + int expect = 2; + + // Add some actual files. + this->FillDirectory(100); + expect += 100; + + typename TestFixture::DirentBufferType dirents(256); + + EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); +} + +// If we lie about the size of the buffer, we should still be able to read the +// entries with the available space. +TYPED_TEST(GetdentsTest, PartialBuffer) { + // . and .. should be in an otherwise empty directory. + int expect = 2; + + // Add some actual files. + this->FillDirectory(100); + expect += 100; + + void* addr = mmap(0, 2 * kPageSize, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + ASSERT_NE(addr, MAP_FAILED); + + char* buf = reinterpret_cast(addr); + + // Guard page + EXPECT_THAT( + mprotect(reinterpret_cast(buf + kPageSize), kPageSize, PROT_NONE), + SyscallSucceeds()); + + // Limit space in buf to 256 bytes. + buf += kPageSize - 256; + + // Lie about the buffer. Even though we claim the buffer is 1 page, + // we should still get all of the dirents in the first 256 bytes. + typename TestFixture::DirentBufferType dirents(buf, 256, kPageSize); + + EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); + + EXPECT_THAT(munmap(addr, 2 * kPageSize), SyscallSucceeds()); +} + +// Open many file descriptors, then scan through /proc/self/fd to find and close +// them all. (The latter is commonly used to handle races betweek fork/execve +// and the creation of unwanted non-O_CLOEXEC file descriptors.) This tests that +// getdents iterates correctly despite mutation of /proc/self/fd. +TYPED_TEST(GetdentsTest, ProcSelfFd) { + constexpr size_t kNfds = 10; + std::unordered_set fds; + std::vector fd_closers; + fd_closers.reserve(fds.size()); + for (int fd : fds) { + fd_closers.emplace_back(fd); + } + for (size_t i = 0; i < kNfds; i++) { + int fd; + ASSERT_THAT(fd = eventfd(0, 0), SyscallSucceeds()); + fds.insert(fd); + } + + const FileDescriptor proc_self_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/fd", O_RDONLY | O_DIRECTORY)); + + // Make the buffer very small since we want to iterate. + typename TestFixture::DirentBufferType dirents( + 2 * sizeof(typename TestFixture::LinuxDirentType)); + std::unordered_set prev_fds; + while (true) { + dirents.Reset(); + int rv; + ASSERT_THAT(rv = RetryEINTR(syscall)(this->SyscallNum(), proc_self_fd.get(), + dirents.Data(), dirents.Size()), + SyscallSucceeds()); + if (rv == 0) { + break; + } + for (auto* d = dirents.Start(rv); d; d = dirents.Next()) { + int dfd; + if (!absl::SimpleAtoi(d->d_name, &dfd)) continue; + EXPECT_TRUE(prev_fds.insert(dfd).second) + << "Repeated observation of /proc/self/fd/" << dfd; + auto it = fds.find(dfd); + if (it != fds.end()) { + fds.erase(it); + EXPECT_THAT(close(dfd), SyscallSucceeds()); + } + } + } + + // Check that we closed every fd. + EXPECT_THAT(fds, ::testing::IsEmpty()); +} + +// Test that getdents returns ENOTDIR when called on a file. +TYPED_TEST(GetdentsTest, NotDir) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + typename TestFixture::DirentBufferType dirents(256); + EXPECT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), + dirents.Size()), + SyscallFailsWithErrno(ENOTDIR)); +} + +// Test that SEEK_SET to 0 causes getdents to re-read the entries. +TYPED_TEST(GetdentsTest, SeekResetsCursor) { + // . and .. should be in an otherwise empty directory. + int expect = 2; + + // Add some files to the directory. + this->FillDirectory(10); + expect += 10; + + typename TestFixture::DirentBufferType dirents(256); + + // We should get all the expected entries. + EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); + + // Seek back to 0. + ASSERT_NO_ERRNO(this->SeekStart()); + + // We should get all the expected entries again. + EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); +} + +// Some tests using the glibc readdir interface. +TEST(ReaddirTest, OpenDir) { + DIR* dev; + ASSERT_THAT(dev = opendir("/dev"), NotNull()); + EXPECT_THAT(closedir(dev), SyscallSucceeds()); +} + +TEST(ReaddirTest, RootContainsBasicDirectories) { + EXPECT_THAT(ListDir("/", true), + IsPosixErrorOkAndHolds(IsSupersetOf( + {"bin", "dev", "etc", "lib", "proc", "sbin", "usr"}))); +} + +TEST(ReaddirTest, Bug24096713Dev) { + auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev", true)); + EXPECT_THAT(contents, Not(IsEmpty())); +} + +TEST(ReaddirTest, Bug24096713ProcTid) { + auto contents = ASSERT_NO_ERRNO_AND_VALUE( + ListDir(absl::StrCat("/proc/", syscall(SYS_gettid), "/"), true)); + EXPECT_THAT(contents, Not(IsEmpty())); +} + +TEST(ReaddirTest, Bug33429925Proc) { + auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc", true)); + EXPECT_THAT(contents, Not(IsEmpty())); +} + +TEST(ReaddirTest, Bug35110122Root) { + auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/", true)); + EXPECT_THAT(contents, Not(IsEmpty())); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/getrandom.cc b/test/syscalls/linux/getrandom.cc new file mode 100644 index 000000000..be5325497 --- /dev/null +++ b/test/syscalls/linux/getrandom.cc @@ -0,0 +1,61 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +#ifndef SYS_getrandom +#if defined(__x86_64__) +#define SYS_getrandom 318 +#elif defined(__i386__) +#define SYS_getrandom 355 +#else +#error "Unknown architecture" +#endif +#endif // SYS_getrandom + +bool SomeByteIsNonZero(char* random_bytes, int length) { + for (int i = 0; i < length; i++) { + if (random_bytes[i] != 0) { + return true; + } + } + return false; +} + +TEST(GetrandomTest, IsRandom) { + // This test calls get_random and makes sure that the array is filled in with + // something that is non-zero. Perhaps we get back \x00\x00\x00\x00\x00.... as + // a random result, but it's so unlikely that we'll just ignore this. + char random_bytes[64] = {}; + int n = syscall(SYS_getrandom, random_bytes, 64, 0); + SKIP_IF(!IsRunningOnGvisor() && n < 0 && errno == ENOSYS); + EXPECT_THAT(n, SyscallSucceeds()); + EXPECT_GT(n, 0); // Some bytes should be returned. + EXPECT_TRUE(SomeByteIsNonZero(random_bytes, n)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/getrusage.cc b/test/syscalls/linux/getrusage.cc new file mode 100644 index 000000000..1ae603858 --- /dev/null +++ b/test/syscalls/linux/getrusage.cc @@ -0,0 +1,177 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/logging.h" +#include "test/util/memory_util.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(GetrusageTest, BasicFork) { + pid_t pid = fork(); + if (pid == 0) { + struct rusage rusage_self; + TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); + struct rusage rusage_children; + TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); + // The child has consumed some memory. + TEST_CHECK(rusage_self.ru_maxrss != 0); + // The child has no children of its own. + TEST_CHECK(rusage_children.ru_maxrss == 0); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); + struct rusage rusage_self; + ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); + struct rusage rusage_children; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); + // The parent has consumed some memory. + EXPECT_GT(rusage_self.ru_maxrss, 0); + // The child has consumed some memory, and because it has exited we can get + // its max RSS. + EXPECT_GT(rusage_children.ru_maxrss, 0); +} + +// Verifies that a process can get the max resident set size of its grandchild, +// i.e. that maxrss propagates correctly from children to waiting parents. +TEST(GetrusageTest, Grandchild) { + constexpr int kGrandchildSizeKb = 1024; + pid_t pid = fork(); + if (pid == 0) { + pid = fork(); + if (pid == 0) { + int flags = MAP_ANONYMOUS | MAP_POPULATE | MAP_PRIVATE; + void *addr = + mmap(nullptr, kGrandchildSizeKb * 1024, PROT_WRITE, flags, -1, 0); + TEST_PCHECK(addr != MAP_FAILED); + } else { + int status; + TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0) == pid); + } + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); + struct rusage rusage_self; + ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); + struct rusage rusage_children; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); + // The parent has consumed some memory. + EXPECT_GT(rusage_self.ru_maxrss, 0); + // The child should consume next to no memory, but the grandchild will + // consume at least 1MB. Verify that usage bubbles up to the grandparent. + EXPECT_GT(rusage_children.ru_maxrss, kGrandchildSizeKb); +} + +// Verifies that processes ignoring SIGCHLD do not have updated child maxrss +// updated. +TEST(GetrusageTest, IgnoreSIGCHLD) { + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa)); + pid_t pid = fork(); + if (pid == 0) { + struct rusage rusage_self; + TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); + // The child has consumed some memory. + TEST_CHECK(rusage_self.ru_maxrss != 0); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), + SyscallFailsWithErrno(ECHILD)); + struct rusage rusage_self; + ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); + struct rusage rusage_children; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); + // The parent has consumed some memory. + EXPECT_GT(rusage_self.ru_maxrss, 0); + // The child's maxrss should not have propagated up. + EXPECT_EQ(rusage_children.ru_maxrss, 0); +} + +// Verifies that zombie processes do not update their parent's maxrss. Only +// reaped processes should do this. +TEST(GetrusageTest, IgnoreZombie) { + pid_t pid = fork(); + if (pid == 0) { + struct rusage rusage_self; + TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); + struct rusage rusage_children; + TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); + // The child has consumed some memory. + TEST_CHECK(rusage_self.ru_maxrss != 0); + // The child has no children of its own. + TEST_CHECK(rusage_children.ru_maxrss == 0); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + // Give the child time to exit. Because we don't call wait, the child should + // remain a zombie. + absl::SleepFor(absl::Seconds(5)); + struct rusage rusage_self; + ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); + struct rusage rusage_children; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); + // The parent has consumed some memory. + EXPECT_GT(rusage_self.ru_maxrss, 0); + // The child has consumed some memory, but hasn't been reaped. + EXPECT_EQ(rusage_children.ru_maxrss, 0); +} + +TEST(GetrusageTest, Wait4) { + pid_t pid = fork(); + if (pid == 0) { + struct rusage rusage_self; + TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); + struct rusage rusage_children; + TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); + // The child has consumed some memory. + TEST_CHECK(rusage_self.ru_maxrss != 0); + // The child has no children of its own. + TEST_CHECK(rusage_children.ru_maxrss == 0); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + struct rusage rusage_children; + int status; + ASSERT_THAT(RetryEINTR(wait4)(pid, &status, 0, &rusage_children), + SyscallSucceeds()); + // The child has consumed some memory, and because it has exited we can get + // its max RSS. + EXPECT_GT(rusage_children.ru_maxrss, 0); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc new file mode 100644 index 000000000..62fc55c72 --- /dev/null +++ b/test/syscalls/linux/inotify.cc @@ -0,0 +1,1489 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { +namespace { + +using ::absl::StreamFormat; +using ::absl::StrFormat; + +constexpr int kBufSize = 1024; + +// C++-friendly version of struct inotify_event. +struct Event { + int32_t wd; + uint32_t mask; + uint32_t cookie; + uint32_t len; + std::string name; + + Event(uint32_t mask, int32_t wd, absl::string_view name, uint32_t cookie) + : wd(wd), + mask(mask), + cookie(cookie), + len(name.size()), + name(std::string(name)) {} + Event(uint32_t mask, int32_t wd, absl::string_view name) + : Event(mask, wd, name, 0) {} + Event(uint32_t mask, int32_t wd) : Event(mask, wd, "", 0) {} + Event() : Event(0, 0, "", 0) {} +}; + +// Prints the symbolic name for a struct inotify_event's 'mask' field. +std::string FlagString(uint32_t flags) { + std::vector names; + +#define EMIT(target) \ + if (flags & target) { \ + names.push_back(#target); \ + flags &= ~target; \ + } + + EMIT(IN_ACCESS); + EMIT(IN_ATTRIB); + EMIT(IN_CLOSE_WRITE); + EMIT(IN_CLOSE_NOWRITE); + EMIT(IN_CREATE); + EMIT(IN_DELETE); + EMIT(IN_DELETE_SELF); + EMIT(IN_MODIFY); + EMIT(IN_MOVE_SELF); + EMIT(IN_MOVED_FROM); + EMIT(IN_MOVED_TO); + EMIT(IN_OPEN); + + EMIT(IN_DONT_FOLLOW); + EMIT(IN_EXCL_UNLINK); + EMIT(IN_ONESHOT); + EMIT(IN_ONLYDIR); + + EMIT(IN_IGNORED); + EMIT(IN_ISDIR); + EMIT(IN_Q_OVERFLOW); + EMIT(IN_UNMOUNT); + +#undef EMIT + + // If we have anything left over at the end, print it as a hex value. + if (flags) { + names.push_back(absl::StrCat("0x", absl::Hex(flags))); + } + + return absl::StrJoin(names, "|"); +} + +std::string DumpEvent(const Event& event) { + return StrFormat( + "%s, wd=%d%s%s", FlagString(event.mask), event.wd, + (event.len > 0) ? StrFormat(", name=%s", event.name) : "", + (event.cookie > 0) ? StrFormat(", cookie=%ud", event.cookie) : ""); +} + +std::string DumpEvents(const std::vector& events, int indent_level) { + std::stringstream ss; + ss << StreamFormat("%d event%s:\n", events.size(), + (events.size() > 1) ? "s" : ""); + int i = 0; + for (const Event& ev : events) { + ss << StreamFormat("%sevents[%d]: %s\n", std::string(indent_level, '\t'), i++, + DumpEvent(ev)); + } + return ss.str(); +} + +// A matcher which takes an expected list of events to match against another +// list of inotify events, in order. This is similar to the ElementsAre matcher, +// but displays more informative messages on mismatch. +class EventsAreMatcher + : public ::testing::MatcherInterface> { + public: + explicit EventsAreMatcher(std::vector references) + : references_(std::move(references)) {} + + bool MatchAndExplain( + std::vector events, + ::testing::MatchResultListener* const listener) const override { + if (references_.size() != events.size()) { + *listener << StreamFormat("\n\tCount mismatch, got %s", + DumpEvents(events, 2)); + return false; + } + + bool success = true; + for (unsigned int i = 0; i < references_.size(); ++i) { + const Event& reference = references_[i]; + const Event& target = events[i]; + + if (target.mask != reference.mask || target.wd != reference.wd || + target.name != reference.name || target.cookie != reference.cookie) { + *listener << StreamFormat("\n\tMismatch at index %d, want %s, got %s,", + i, DumpEvent(reference), DumpEvent(target)); + success = false; + } + } + + if (!success) { + *listener << StreamFormat("\n\tIn total of %s", DumpEvents(events, 2)); + } + return success; + } + + void DescribeTo(::std::ostream* const os) const override { + *os << StreamFormat("%s", DumpEvents(references_, 1)); + } + + void DescribeNegationTo(::std::ostream* const os) const override { + *os << StreamFormat("mismatch from %s", DumpEvents(references_, 1)); + } + + private: + std::vector references_; +}; + +::testing::Matcher> Are(std::vector events) { + return MakeMatcher(new EventsAreMatcher(std::move(events))); +} + +// Similar to the EventsAre matcher, but the order of events are ignored. +class UnorderedEventsAreMatcher + : public ::testing::MatcherInterface> { + public: + explicit UnorderedEventsAreMatcher(std::vector references) + : references_(std::move(references)) {} + + bool MatchAndExplain( + std::vector events, + ::testing::MatchResultListener* const listener) const override { + if (references_.size() != events.size()) { + *listener << StreamFormat("\n\tCount mismatch, got %s", + DumpEvents(events, 2)); + return false; + } + + std::vector unmatched(references_); + + for (const Event& candidate : events) { + for (auto it = unmatched.begin(); it != unmatched.end();) { + const Event& reference = *it; + if (candidate.mask == reference.mask && candidate.wd == reference.wd && + candidate.name == reference.name && + candidate.cookie == reference.cookie) { + it = unmatched.erase(it); + break; + } else { + ++it; + } + } + } + + // Anything left unmatched? If so, the matcher fails. + if (!unmatched.empty()) { + *listener << StreamFormat("\n\tFailed to match %s", + DumpEvents(unmatched, 2)); + *listener << StreamFormat("\n\tIn total of %s", DumpEvents(events, 2)); + return false; + } + + return true; + } + + void DescribeTo(::std::ostream* const os) const override { + *os << StreamFormat("unordered %s", DumpEvents(references_, 1)); + } + + void DescribeNegationTo(::std::ostream* const os) const override { + *os << StreamFormat("mismatch from unordered %s", + DumpEvents(references_, 1)); + } + + private: + std::vector references_; +}; + +::testing::Matcher> AreUnordered(std::vector events) { + return MakeMatcher(new UnorderedEventsAreMatcher(std::move(events))); +} + +// Reads events from an inotify fd until either EOF, or read returns EAGAIN. +PosixErrorOr> DrainEvents(int fd) { + std::vector events; + while (true) { + int events_size = 0; + if (ioctl(fd, FIONREAD, &events_size) < 0) { + return PosixError(errno, "ioctl(FIONREAD) failed on inotify fd"); + } + // Deliberately use a buffer that is larger than necessary, expecting to + // only read events_size bytes. + std::vector buf(events_size + kBufSize, 0); + const ssize_t readlen = read(fd, buf.data(), buf.size()); + MaybeSave(); + // Read error? + if (readlen < 0) { + if (errno == EAGAIN) { + // If EAGAIN, no more events at the moment. Return what we have so far. + return events; + } + // Some other read error. Return an error. Right now if we encounter this + // after already reading some events, they get lost. However, we don't + // expect to see any error, and the calling test will fail immediately if + // we signal an error anyways, so this is acceptable. + return PosixError(errno, "read() failed on inotify fd"); + } + if (readlen < static_cast(sizeof(struct inotify_event))) { + // Impossibly short read. + return PosixError( + EIO, + "read() didn't return enough data represent even a single event"); + } + if (readlen != events_size) { + return PosixError(EINVAL, absl::StrCat("read ", readlen, + " bytes, expected ", events_size)); + } + if (readlen == 0) { + // EOF. + return events; + } + + // Normal read. + const char* cursor = buf.data(); + while (cursor < (buf.data() + readlen)) { + struct inotify_event event = {}; + memcpy(&event, cursor, sizeof(struct inotify_event)); + + Event ev; + ev.wd = event.wd; + ev.mask = event.mask; + ev.cookie = event.cookie; + ev.len = event.len; + if (event.len > 0) { + TEST_CHECK(static_cast(sizeof(struct inotify_event) + event.len) <= + readlen); + ev.name = + std::string(cursor + offsetof(struct inotify_event, name)); // NOLINT + // Name field should always be smaller than event.len, otherwise we have + // a buffer overflow. The two sizes aren't equal because the std::string + // constructor will stop at the first null byte, while event.name may be + // padded up to event.len using multiple null bytes. + TEST_CHECK(ev.name.size() <= event.len); + } + + events.push_back(ev); + cursor += sizeof(struct inotify_event) + event.len; + } + } +} + +PosixErrorOr InotifyInit1(int flags) { + int fd; + EXPECT_THAT(fd = inotify_init1(flags), SyscallSucceeds()); + if (fd < 0) { + return PosixError(errno, "inotify_init1() failed"); + } + return FileDescriptor(fd); +} + +PosixErrorOr InotifyAddWatch(int fd, const std::string& path, uint32_t mask) { + int wd; + EXPECT_THAT(wd = inotify_add_watch(fd, path.c_str(), mask), + SyscallSucceeds()); + if (wd < 0) { + return PosixError(errno, "inotify_add_watch() failed"); + } + return wd; +} + +TEST(Inotify, InotifyFdNotWritable) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); + EXPECT_THAT(write(fd.get(), "x", 1), SyscallFailsWithErrno(EBADF)); +} + +TEST(Inotify, NonBlockingReadReturnsEagain) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + std::vector buf(kBufSize, 0); + + // The read below should return fail with EAGAIN because there is no data to + // read and we've specified IN_NONBLOCK. We're guaranteed that there is no + // data to read because we haven't registered any watches yet. + EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(Inotify, AddWatchOnInvalidFdFails) { + // Garbage fd. + EXPECT_THAT(inotify_add_watch(-1, "/tmp", IN_ALL_EVENTS), + SyscallFailsWithErrno(EBADF)); + EXPECT_THAT(inotify_add_watch(1337, "/tmp", IN_ALL_EVENTS), + SyscallFailsWithErrno(EBADF)); + + // Non-inotify fds. + EXPECT_THAT(inotify_add_watch(0, "/tmp", IN_ALL_EVENTS), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(inotify_add_watch(1, "/tmp", IN_ALL_EVENTS), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(inotify_add_watch(2, "/tmp", IN_ALL_EVENTS), + SyscallFailsWithErrno(EINVAL)); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/tmp", O_RDONLY)); + EXPECT_THAT(inotify_add_watch(fd.get(), "/tmp", IN_ALL_EVENTS), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(Inotify, RemovingWatchGeneratesEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); + + // Read events, ensure the first event is IN_IGNORED. + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + EXPECT_THAT(events, Are({Event(IN_IGNORED, wd)})); +} + +TEST(Inotify, CanDeleteFileAfterRemovingWatch) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); + file1.reset(); +} + +TEST(Inotify, CanRemoveWatchAfterDeletingFile) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + file1.reset(); + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd), Event(IN_DELETE_SELF, wd), + Event(IN_IGNORED, wd)})); + + EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallFailsWithErrno(EINVAL)); +} + +TEST(Inotify, DuplicateWatchRemovalFails) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); + EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallFailsWithErrno(EINVAL)); +} + +TEST(Inotify, ConcurrentFileDeletionAndWatchRemoval) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const std::string filename = NewTempAbsPathInDir(root.path()); + + auto file_create_delete = [filename]() { + const DisableSave ds; // Too expensive. + for (int i = 0; i < 100; ++i) { + FileDescriptor file_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT, S_IRUSR | S_IWUSR)); + file_fd.reset(); // Close before unlinking (although save is disabled). + EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds()); + } + }; + + const int shared_fd = fd.get(); // We need to pass it to the thread. + auto add_remove_watch = [shared_fd, filename]() { + for (int i = 0; i < 100; ++i) { + int wd = inotify_add_watch(shared_fd, filename.c_str(), IN_ALL_EVENTS); + MaybeSave(); + if (wd != -1) { + // Watch added successfully, try removal. + if (inotify_rm_watch(shared_fd, wd)) { + // If removal fails, the only acceptable reason is if the wd + // is invalid, which will be the case if we try to remove + // the watch after the file has been deleted. + EXPECT_EQ(errno, EINVAL); + } + } else { + // Add watch failed, this should only fail if the target file doesn't + // exist. + EXPECT_EQ(errno, ENOENT); + } + } + }; + + ScopedThread t1(file_create_delete); + ScopedThread t2(add_remove_watch); +} + +TEST(Inotify, DeletingChildGeneratesEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + const std::string file1_path = file1.reset(); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + AreUnordered({Event(IN_ATTRIB, file1_wd), Event(IN_DELETE_SELF, file1_wd), + Event(IN_IGNORED, file1_wd), + Event(IN_DELETE, root_wd, Basename(file1_path))})); +} + +TEST(Inotify, CreatingFileGeneratesEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + // Create a new file in the directory. + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + + // The library function we use to create the new file opens it for writing to + // create it and sets permissions on it, so we expect the three extra events. + ASSERT_THAT(events, Are({Event(IN_CREATE, wd, Basename(file1.path())), + Event(IN_OPEN, wd, Basename(file1.path())), + Event(IN_CLOSE_WRITE, wd, Basename(file1.path())), + Event(IN_ATTRIB, wd, Basename(file1.path()))})); +} + +TEST(Inotify, ReadingFileGeneratesAccessEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), "some content", TempPath::kDefaultFileMode)); + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + char buf; + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_ACCESS, wd, Basename(file1.path()))})); +} + +TEST(Inotify, WritingFileGeneratesModifyEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + const std::string data = "some content"; + EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), + SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_MODIFY, wd, Basename(file1.path()))})); +} + +TEST(Inotify, WatchSetAfterOpenReportsCloseFdEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + FileDescriptor file1_fd_writable = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); + FileDescriptor file1_fd_not_writable = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + file1_fd_writable.reset(); // Close file1_fd_writable. + std::vector events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_CLOSE_WRITE, wd, Basename(file1.path()))})); + + file1_fd_not_writable.reset(); // Close file1_fd_not_writable. + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, + Are({Event(IN_CLOSE_NOWRITE, wd, Basename(file1.path()))})); +} + +TEST(Inotify, ChildrenDeletionInWatchedDirGeneratesEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + TempPath dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + const std::string file1_path = file1.reset(); + const std::string dir1_path = dir1.release(); + EXPECT_THAT(rmdir(dir1_path.c_str()), SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + + ASSERT_THAT(events, + Are({Event(IN_DELETE, wd, Basename(file1_path)), + Event(IN_DELETE | IN_ISDIR, wd, Basename(dir1_path))})); +} + +TEST(Inotify, WatchTargetDeletionGeneratesEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + EXPECT_THAT(rmdir(root.path().c_str()), SyscallSucceeds()); + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_DELETE_SELF, wd), Event(IN_IGNORED, wd)})); +} + +TEST(Inotify, MoveGeneratesEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const TempPath dir1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); + const TempPath dir2 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); + + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int dir1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), dir1.path(), IN_ALL_EVENTS)); + const int dir2_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), dir2.path(), IN_ALL_EVENTS)); + // Test move from root -> root. + std::string newpath = NewTempAbsPathInDir(root.path()); + std::string oldpath = file1.release(); + EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); + file1.reset(newpath); + std::vector events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), + Event(IN_MOVED_TO, root_wd, Basename(newpath), events[1].cookie)})); + EXPECT_NE(events[0].cookie, 0); + EXPECT_EQ(events[0].cookie, events[1].cookie); + uint32_t last_cookie = events[0].cookie; + + // Test move from root -> root/dir1. + newpath = NewTempAbsPathInDir(dir1.path()); + oldpath = file1.release(); + EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); + file1.reset(newpath); + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), + Event(IN_MOVED_TO, dir1_wd, Basename(newpath), events[1].cookie)})); + // Cookies should be distinct between distinct rename events. + EXPECT_NE(events[0].cookie, last_cookie); + EXPECT_EQ(events[0].cookie, events[1].cookie); + last_cookie = events[0].cookie; + + // Test move from root/dir1 -> root/dir2. + newpath = NewTempAbsPathInDir(dir2.path()); + oldpath = file1.release(); + EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); + file1.reset(newpath); + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + Are({Event(IN_MOVED_FROM, dir1_wd, Basename(oldpath), events[0].cookie), + Event(IN_MOVED_TO, dir2_wd, Basename(newpath), events[1].cookie)})); + EXPECT_NE(events[0].cookie, last_cookie); + EXPECT_EQ(events[0].cookie, events[1].cookie); + last_cookie = events[0].cookie; +} + +TEST(Inotify, MoveWatchedTargetGeneratesEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + const std::string newpath = NewTempAbsPathInDir(root.path()); + const std::string oldpath = file1.release(); + EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); + file1.reset(newpath); + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), + Event(IN_MOVED_TO, root_wd, Basename(newpath), events[1].cookie), + // Self move events do not have a cookie. + Event(IN_MOVE_SELF, file1_wd)})); + EXPECT_NE(events[0].cookie, 0); + EXPECT_EQ(events[0].cookie, events[1].cookie); +} + +TEST(Inotify, CoalesceEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), "some content", TempPath::kDefaultFileMode)); + + FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + // Read the file a few times. This will would generate multiple IN_ACCESS + // events but they should get coalesced to a single event. + char buf; + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + + // Use the close event verify that we haven't simply left the additional + // IN_ACCESS events unread. + file1_fd.reset(); // Close file1_fd. + + const std::string file1_name = std::string(Basename(file1.path())); + std::vector events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_ACCESS, wd, file1_name), + Event(IN_CLOSE_NOWRITE, wd, file1_name)})); + + // Now let's try interleaving other events into a stream of repeated events. + file1_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); + + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); + EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); + EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + + file1_fd.reset(); // Close the file. + + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + Are({Event(IN_OPEN, wd, file1_name), Event(IN_ACCESS, wd, file1_name), + Event(IN_MODIFY, wd, file1_name), Event(IN_ACCESS, wd, file1_name), + Event(IN_CLOSE_WRITE, wd, file1_name)})); + + // Ensure events aren't coalesced if they are from different files. + const TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), "some content", TempPath::kDefaultFileMode)); + // Discard events resulting from creation of file2. + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + + file1_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + FileDescriptor file2_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file2.path(), O_RDONLY)); + + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file2_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + + // Close both files. + file1_fd.reset(); + file2_fd.reset(); + + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + const std::string file2_name = std::string(Basename(file2.path())); + ASSERT_THAT( + events, + Are({Event(IN_OPEN, wd, file1_name), Event(IN_OPEN, wd, file2_name), + Event(IN_ACCESS, wd, file1_name), Event(IN_ACCESS, wd, file2_name), + Event(IN_ACCESS, wd, file1_name), + Event(IN_CLOSE_NOWRITE, wd, file1_name), + Event(IN_CLOSE_NOWRITE, wd, file2_name)})); +} + +TEST(Inotify, ClosingInotifyFdWithoutRemovingWatchesWorks) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + // Note: The check on close will happen in FileDescriptor::~FileDescriptor(). +} + +TEST(Inotify, NestedWatches) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), "some content", TempPath::kDefaultFileMode)); + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + // Read from file1. This should generate an event for both watches. + char buf; + EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_ACCESS, root_wd, Basename(file1.path())), + Event(IN_ACCESS, file1_wd)})); +} + +TEST(Inotify, ConcurrentThreadsGeneratingEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + std::vector files; + files.reserve(10); + for (int i = 0; i < 10; i++) { + files.emplace_back(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), "some content", TempPath::kDefaultFileMode))); + } + + auto test_thread = [&files]() { + uint32_t seed = time(nullptr); + for (int i = 0; i < 20; i++) { + const TempPath& file = files[rand_r(&seed) % files.size()]; + const FileDescriptor file_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); + TEST_PCHECK(write(file_fd.get(), "x", 1) == 1); + } + }; + + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + std::list threads; + for (int i = 0; i < 3; i++) { + threads.emplace_back(test_thread); + } + for (auto& t : threads) { + t.Join(); + } + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + // 3 threads doing 20 iterations, 3 events per iteration (open, write, + // close). However, some events may be coalesced, and we can't reliably + // predict how they'll be coalesced since the test threads aren't + // synchronized. We can only check that we aren't getting unexpected events. + for (const Event& ev : events) { + EXPECT_NE(ev.mask & (IN_OPEN | IN_MODIFY | IN_CLOSE_WRITE), 0); + } +} + +TEST(Inotify, ReadWithTooSmallBufferFails) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + // Open the file to queue an event. This event will not have a filename, so + // reading from the inotify fd should return sizeof(struct inotify_event) + // bytes of data. + FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + std::vector buf(kBufSize, 0); + ssize_t readlen; + + // Try a buffer too small to hold any potential event. This is rejected + // outright without the event being dequeued. + EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event) - 1), + SyscallFailsWithErrno(EINVAL)); + // Try a buffer just large enough. This should succeeed. + EXPECT_THAT( + readlen = read(fd.get(), buf.data(), sizeof(struct inotify_event)), + SyscallSucceeds()); + EXPECT_EQ(readlen, sizeof(struct inotify_event)); + // Event queue is now empty, the next read should return EAGAIN. + EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), + SyscallFailsWithErrno(EAGAIN)); + + // Now put a watch on the directory, so that generated events contain a name. + EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); + + // Drain the event generated from the watch removal. + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + file1_fd.reset(); // Close file to generate an event. + + // Try a buffer too small to hold any event and one too small to hold an event + // with a name. These should both fail without consuming the event. + EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event) - 1), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), + SyscallFailsWithErrno(EINVAL)); + // Now try with a large enough buffer. This should return the one event. + EXPECT_THAT(readlen = read(fd.get(), buf.data(), buf.size()), + SyscallSucceeds()); + EXPECT_GE(readlen, + sizeof(struct inotify_event) + Basename(file1.path()).size()); + // With the single event read, the queue should once again be empty. + EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(Inotify, BlockingReadOnInotifyFd) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); + const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), "some content", TempPath::kDefaultFileMode)); + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + // Spawn a thread performing a blocking read for new events on the inotify fd. + std::vector buf(kBufSize, 0); + const int shared_fd = fd.get(); // The thread needs it. + ScopedThread t([shared_fd, &buf]() { + ssize_t readlen; + EXPECT_THAT(readlen = read(shared_fd, buf.data(), buf.size()), + SyscallSucceeds()); + }); + + // Perform a read on the watched file, which should generate an IN_ACCESS + // event, unblocking the event_reader thread. + char c; + EXPECT_THAT(read(file1_fd.get(), &c, 1), SyscallSucceeds()); + + // Wait for the thread to read the event and exit. + t.Join(); + + // Make sure the event we got back is sane. + uint32_t event_mask; + memcpy(&event_mask, buf.data() + offsetof(struct inotify_event, mask), + sizeof(event_mask)); + EXPECT_EQ(event_mask, IN_ACCESS); +} + +TEST(Inotify, WatchOnRelativePath) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), "some content", TempPath::kDefaultFileMode)); + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); + + // Change working directory to root. + const char* old_working_dir = get_current_dir_name(); + EXPECT_THAT(chdir(root.path().c_str()), SyscallSucceeds()); + + // Add a watch on file1 with a relative path. + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), std::string(Basename(file1.path())), IN_ALL_EVENTS)); + + // Perform a read on file1, this should generate an IN_ACCESS event. + char c; + EXPECT_THAT(read(file1_fd.get(), &c, 1), SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + EXPECT_THAT(events, Are({Event(IN_ACCESS, wd)})); + + // Explicitly reset the working directory so that we don't continue to + // reference "root". Once the test ends, "root" will get unlinked. If we + // continue to hold a reference, random save/restore tests can fail if a save + // is triggered after "root" is unlinked; we can't save deleted fs objects + // with active references. + EXPECT_THAT(chdir(old_working_dir), SyscallSucceeds()); +} + +TEST(Inotify, ZeroLengthReadWriteDoesNotGenerateEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const char kContent[] = "some content"; + TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + root.path(), kContent, TempPath::kDefaultFileMode)); + const int kContentSize = sizeof(kContent) - 1; + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + std::vector buf(kContentSize, 0); + // Read all available data. + ssize_t readlen; + EXPECT_THAT(readlen = read(file1_fd.get(), buf.data(), kContentSize), + SyscallSucceeds()); + EXPECT_EQ(readlen, kContentSize); + // Drain all events and make sure we got the IN_ACCESS for the read. + std::vector events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + EXPECT_THAT(events, Are({Event(IN_ACCESS, wd, Basename(file1.path()))})); + + // Now try read again. This should be a 0-length read, since we're at EOF. + char c; + EXPECT_THAT(readlen = read(file1_fd.get(), &c, 1), SyscallSucceeds()); + EXPECT_EQ(readlen, 0); + // We should have no new events. + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + EXPECT_TRUE(events.empty()); + + // Try issuing a zero-length read. + EXPECT_THAT(readlen = read(file1_fd.get(), &c, 0), SyscallSucceeds()); + EXPECT_EQ(readlen, 0); + // We should have no new events. + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + EXPECT_TRUE(events.empty()); + + // Try issuing a zero-length write. + ssize_t writelen; + EXPECT_THAT(writelen = write(file1_fd.get(), &c, 0), SyscallSucceeds()); + EXPECT_EQ(writelen, 0); + // We should have no new events. + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + EXPECT_TRUE(events.empty()); +} + +TEST(Inotify, ChmodGeneratesAttribEvent_NoRandomSave) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const FileDescriptor root_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(root.path(), O_RDONLY)); + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + auto verify_chmod_events = [&]() { + std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_ATTRIB, root_wd, Basename(file1.path())), + Event(IN_ATTRIB, file1_wd)})); + }; + + // Don't do cooperative S/R tests for any of the {f}chmod* syscalls below, the + // test will always fail because nodes cannot be saved when they have stricted + // permissions than the original host node. + const DisableSave ds; + + // Chmod. + ASSERT_THAT(chmod(file1.path().c_str(), S_IWGRP), SyscallSucceeds()); + verify_chmod_events(); + + // Fchmod. + ASSERT_THAT(fchmod(file1_fd.get(), S_IRGRP | S_IWGRP), SyscallSucceeds()); + verify_chmod_events(); + + // Fchmodat. + const std::string file1_basename = std::string(Basename(file1.path())); + ASSERT_THAT(fchmodat(root_fd.get(), file1_basename.c_str(), S_IWGRP, 0), + SyscallSucceeds()); + verify_chmod_events(); +} + +TEST(Inotify, TruncateGeneratesModifyEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + auto verify_truncate_events = [&]() { + std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_MODIFY, root_wd, Basename(file1.path())), + Event(IN_MODIFY, file1_wd)})); + }; + + // Truncate. + EXPECT_THAT(truncate(file1.path().c_str(), 4096), SyscallSucceeds()); + verify_truncate_events(); + + // Ftruncate. + EXPECT_THAT(ftruncate(file1_fd.get(), 8192), SyscallSucceeds()); + verify_truncate_events(); + + // No events if truncate fails. + EXPECT_THAT(ftruncate(file1_fd.get(), -1), SyscallFailsWithErrno(EINVAL)); + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({})); +} + +TEST(Inotify, GetdentsGeneratesAccessEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + // This internally calls getdents(2). We also expect to see an open/close + // event for the dirfd. + ASSERT_NO_ERRNO_AND_VALUE(ListDir(root.path(), false)); + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + + // Linux only seems to generate access events on getdents() on some + // calls. Allow the test to pass even if it isn't generated. gVisor will + // always generate the IN_ACCESS event so the test will at least ensure gVisor + // behaves reasonably. + int i = 0; + EXPECT_EQ(events[i].mask, IN_OPEN | IN_ISDIR); + ++i; + if (IsRunningOnGvisor()) { + EXPECT_EQ(events[i].mask, IN_ACCESS | IN_ISDIR); + ++i; + } else { + if (events[i].mask == (IN_ACCESS | IN_ISDIR)) { + // Skip over the IN_ACCESS event on Linux, it only shows up some of the + // time so we can't assert its existence. + ++i; + } + } + EXPECT_EQ(events[i].mask, IN_CLOSE_NOWRITE | IN_ISDIR); +} + +TEST(Inotify, MknodGeneratesCreateEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + const TempPath file1(root.path() + "/file1"); + const int rc = mknod(file1.path().c_str(), S_IFREG, 0); + // mknod(2) is only supported on tmpfs in the sandbox. + SKIP_IF(IsRunningOnGvisor() && rc != 0); + ASSERT_THAT(rc, SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_CREATE, wd, Basename(file1.path()))})); +} + +TEST(Inotify, SymlinkGeneratesCreateEvent) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const TempPath link1(NewTempAbsPathInDir(root.path())); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + ASSERT_THAT(symlink(file1.path().c_str(), link1.path().c_str()), + SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + + ASSERT_THAT(events, Are({Event(IN_CREATE, root_wd, Basename(link1.path()))})); +} + +TEST(Inotify, LinkGeneratesAttribAndCreateEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const TempPath link1(root.path() + "/link1"); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + const int rc = link(file1.path().c_str(), link1.path().c_str()); + // link(2) is only supported on tmpfs in the sandbox. + SKIP_IF(IsRunningOnGvisor() && rc != 0 && errno == EPERM); + ASSERT_THAT(rc, SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_ATTRIB, file1_wd), + Event(IN_CREATE, root_wd, Basename(link1.path()))})); +} + +TEST(Inotify, HardlinksReuseSameWatch) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + TempPath link1(root.path() + "/link1"); + const int rc = link(file1.path().c_str(), link1.path().c_str()); + // link(2) is only supported on tmpfs in the sandbox. + SKIP_IF(IsRunningOnGvisor() && rc != 0 && errno == EPERM); + ASSERT_THAT(rc, SyscallSucceeds()); + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + const int link1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), link1.path(), IN_ALL_EVENTS)); + + // The watch descriptors for watches on different links to the same file + // should be identical. + EXPECT_NE(root_wd, file1_wd); + EXPECT_EQ(file1_wd, link1_wd); + + FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); + + std::vector events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, + AreUnordered({Event(IN_OPEN, root_wd, Basename(file1.path())), + Event(IN_OPEN, file1_wd)})); + + // For the next step, we want to ensure all fds to the file are closed. Do + // that now and drain the resulting events. + file1_fd.reset(); + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, + Are({Event(IN_CLOSE_WRITE, root_wd, Basename(file1.path())), + Event(IN_CLOSE_WRITE, file1_wd)})); + + // Try removing the link and let's see what events show up. Note that after + // this, we still have a link to the file so the watch shouldn't be + // automatically removed. + const std::string link1_path = link1.reset(); + + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_ATTRIB, link1_wd), + Event(IN_DELETE, root_wd, Basename(link1_path))})); + + // Now remove the other link. Since this is the last link to the file, the + // watch should be automatically removed. + const std::string file1_path = file1.reset(); + + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + AreUnordered({Event(IN_ATTRIB, file1_wd), Event(IN_DELETE_SELF, file1_wd), + Event(IN_IGNORED, file1_wd), + Event(IN_DELETE, root_wd, Basename(file1_path))})); +} + +TEST(Inotify, MkdirGeneratesCreateEventWithDirFlag) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + + const TempPath dir1(NewTempAbsPathInDir(root.path())); + ASSERT_THAT(mkdir(dir1.path().c_str(), 0777), SyscallSucceeds()); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT( + events, + Are({Event(IN_CREATE | IN_ISDIR, root_wd, Basename(dir1.path()))})); +} + +TEST(Inotify, MultipleInotifyInstancesAndWatchesAllGetEvents) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); + constexpr int kNumFds = 30; + std::vector inotify_fds; + + for (int i = 0; i < kNumFds; ++i) { + const DisableSave ds; // Too expensive. + inotify_fds.emplace_back( + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK))); + const FileDescriptor& fd = inotify_fds[inotify_fds.size() - 1]; // Back. + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + } + + const std::string data = "some content"; + EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), + SyscallSucceeds()); + + for (const FileDescriptor& fd : inotify_fds) { + const DisableSave ds; // Too expensive. + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + if (events.size() >= 2) { + EXPECT_EQ(events[0].mask, IN_MODIFY); + EXPECT_EQ(events[0].wd, 1); + EXPECT_EQ(events[0].name, Basename(file1.path())); + EXPECT_EQ(events[1].mask, IN_MODIFY); + EXPECT_EQ(events[1].wd, 2); + EXPECT_EQ(events[1].name, ""); + } + } +} + +TEST(Inotify, EventsGoUpAtMostOneLevel) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath dir1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); + const int dir1_wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), dir1.path(), IN_ALL_EVENTS)); + + const std::string file1_path = file1.reset(); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_DELETE, dir1_wd, Basename(file1_path))})); +} + +TEST(Inotify, DuplicateWatchReturnsSameWatchDescriptor) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int wd1 = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + const int wd2 = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); + + EXPECT_EQ(wd1, wd2); + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + // The watch shouldn't be duplicated, we only expect one event. + ASSERT_THAT(events, Are({Event(IN_OPEN, wd1)})); +} + +TEST(Inotify, UnmatchedEventsAreDiscarded) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch(fd.get(), file1.path(), IN_ACCESS)); + + const FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); + + const std::vector events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + // We only asked for access events, the open event should be discarded. + ASSERT_THAT(events, Are({})); +} + +TEST(Inotify, AddWatchWithInvalidEventMaskFails) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + EXPECT_THAT(inotify_add_watch(fd.get(), root.path().c_str(), 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(Inotify, AddWatchOnInvalidPathFails) { + const TempPath nonexistent(NewTempAbsPath()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + // Non-existent path. + EXPECT_THAT( + inotify_add_watch(fd.get(), nonexistent.path().c_str(), IN_CREATE), + SyscallFailsWithErrno(ENOENT)); + + // Garbage path pointer. + EXPECT_THAT(inotify_add_watch(fd.get(), nullptr, IN_CREATE), + SyscallFailsWithErrno(EFAULT)); +} + +TEST(Inotify, InOnlyDirFlagRespected) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + EXPECT_THAT( + inotify_add_watch(fd.get(), root.path().c_str(), IN_ACCESS | IN_ONLYDIR), + SyscallSucceeds()); + + EXPECT_THAT( + inotify_add_watch(fd.get(), file1.path().c_str(), IN_ACCESS | IN_ONLYDIR), + SyscallFailsWithErrno(ENOTDIR)); +} + +TEST(Inotify, MaskAddMergesWithExistingEventMask) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + FileDescriptor file1_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_OPEN | IN_CLOSE_WRITE)); + + const std::string data = "some content"; + EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), + SyscallSucceeds()); + + // We shouldn't get any events, since IN_MODIFY wasn't in the event mask. + std::vector events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({})); + + // Add IN_MODIFY to event mask. + ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(fd.get(), file1.path(), IN_MODIFY | IN_MASK_ADD)); + + EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), + SyscallSucceeds()); + + // This time we should get the modify event. + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_MODIFY, wd)})); + + // Now close the fd. If the modify event was added to the event mask rather + // than replacing the event mask we won't get the close event. + file1_fd.reset(); + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); + ASSERT_THAT(events, Are({Event(IN_CLOSE_WRITE, wd)})); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/ioctl.cc b/test/syscalls/linux/ioctl.cc new file mode 100644 index 000000000..bee0ba1b3 --- /dev/null +++ b/test/syscalls/linux/ioctl.cc @@ -0,0 +1,375 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +bool CheckNonBlocking(int fd) { + int ret = fcntl(fd, F_GETFL, 0); + TEST_CHECK(ret != -1); + return (ret & O_NONBLOCK) == O_NONBLOCK; +} + +bool CheckCloExec(int fd) { + int ret = fcntl(fd, F_GETFD, 0); + TEST_CHECK(ret != -1); + return (ret & FD_CLOEXEC) == FD_CLOEXEC; +} + +class IoctlTest : public ::testing::Test { + protected: + void SetUp() override { + ASSERT_THAT(fd_ = open("/dev/null", O_RDONLY), SyscallSucceeds()); + } + + void TearDown() override { + if (fd_ >= 0) { + ASSERT_THAT(close(fd_), SyscallSucceeds()); + fd_ = -1; + } + } + + int fd() const { return fd_; } + + private: + int fd_ = -1; +}; + +TEST_F(IoctlTest, BadFileDescriptor) { + EXPECT_THAT(ioctl(-1 /* fd */, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(IoctlTest, InvalidControlNumber) { + EXPECT_THAT(ioctl(STDOUT_FILENO, 0), SyscallFailsWithErrno(ENOTTY)); +} + +TEST_F(IoctlTest, FIONBIOSucceeds) { + EXPECT_FALSE(CheckNonBlocking(fd())); + int set = 1; + EXPECT_THAT(ioctl(fd(), FIONBIO, &set), SyscallSucceeds()); + EXPECT_TRUE(CheckNonBlocking(fd())); + set = 0; + EXPECT_THAT(ioctl(fd(), FIONBIO, &set), SyscallSucceeds()); + EXPECT_FALSE(CheckNonBlocking(fd())); +} + +TEST_F(IoctlTest, FIONBIOFails) { + EXPECT_THAT(ioctl(fd(), FIONBIO, nullptr), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(IoctlTest, FIONCLEXSucceeds) { + EXPECT_THAT(ioctl(fd(), FIONCLEX), SyscallSucceeds()); + EXPECT_FALSE(CheckCloExec(fd())); +} + +TEST_F(IoctlTest, FIOCLEXSucceeds) { + EXPECT_THAT(ioctl(fd(), FIOCLEX), SyscallSucceeds()); + EXPECT_TRUE(CheckCloExec(fd())); +} + +TEST_F(IoctlTest, FIOASYNCFails) { + EXPECT_THAT(ioctl(fd(), FIOASYNC, nullptr), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(IoctlTest, FIOASYNCSucceeds) { + // Not all FDs support FIOASYNC. + const FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + int before = -1; + ASSERT_THAT(before = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + + int set = 1; + EXPECT_THAT(ioctl(s.get(), FIOASYNC, &set), SyscallSucceeds()); + + int after_set = -1; + ASSERT_THAT(after_set = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(after_set, before | O_ASYNC) << "before was " << before; + + set = 0; + EXPECT_THAT(ioctl(s.get(), FIOASYNC, &set), SyscallSucceeds()); + + ASSERT_THAT(fcntl(s.get(), F_GETFL), SyscallSucceedsWithValue(before)); +} + +/* Count of the number of SIGIOs handled. */ +static volatile int io_received = 0; + +void inc_io_handler(int sig, siginfo_t* siginfo, void* arg) { io_received++; } + +TEST_F(IoctlTest, FIOASYNCNoTarget) { + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + // Count SIGIOs received. + io_received = 0; + struct sigaction sa; + sa.sa_sigaction = inc_io_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); + + // Actually allow SIGIO delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); + + int set = 1; + EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); + + constexpr char kData[] = "abc"; + ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), + SyscallSucceedsWithValue(sizeof(kData))); + + EXPECT_EQ(io_received, 0); +} + +TEST_F(IoctlTest, FIOASYNCSelfTarget) { + // FIXME: gVisor erroneously sends SIGIO on close(2), which would + // kill the test when pair goes out of scope. Temporarily ignore SIGIO so that + // that the close signal is ignored. + struct sigaction sa; + sa.sa_handler = SIG_IGN; + auto early_sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + // Count SIGIOs received. + io_received = 0; + sa.sa_sigaction = inc_io_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); + + // Actually allow SIGIO delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); + + int set = 1; + EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); + + pid_t pid = getpid(); + EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); + + constexpr char kData[] = "abc"; + ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), + SyscallSucceedsWithValue(sizeof(kData))); + + EXPECT_EQ(io_received, 1); +} + +// Equivalent to FIOASYNCSelfTarget except that FIOSETOWN is called before +// FIOASYNC. +TEST_F(IoctlTest, FIOASYNCSelfTarget2) { + // FIXME: gVisor erroneously sends SIGIO on close(2), which would + // kill the test when pair goes out of scope. Temporarily ignore SIGIO so that + // that the close signal is ignored. + struct sigaction sa; + sa.sa_handler = SIG_IGN; + auto early_sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + // Count SIGIOs received. + io_received = 0; + sa.sa_sigaction = inc_io_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); + + // Actually allow SIGIO delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); + + pid_t pid = getpid(); + EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); + + int set = 1; + EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); + + constexpr char kData[] = "abc"; + ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), + SyscallSucceedsWithValue(sizeof(kData))); + + EXPECT_EQ(io_received, 1); +} + +TEST_F(IoctlTest, FIOASYNCInvalidPID) { + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + int set = 1; + ASSERT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); + pid_t pid = INT_MAX; + // This succeeds (with behavior equivalent to a pid of 0) in Linux prior to + // f73127356f34 "fs/fcntl: return -ESRCH in f_setown when pid/pgid can't be + // found", and fails with EPERM after that commit. + EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), + AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ESRCH))); +} + +TEST_F(IoctlTest, FIOASYNCUnsetTarget) { + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + // Count SIGIOs received. + io_received = 0; + struct sigaction sa; + sa.sa_sigaction = inc_io_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); + + // Actually allow SIGIO delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); + + int set = 1; + EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); + + pid_t pid = getpid(); + EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); + + // Passing a PID of 0 unsets the target. + pid = 0; + EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); + + constexpr char kData[] = "abc"; + ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), + SyscallSucceedsWithValue(sizeof(kData))); + + EXPECT_EQ(io_received, 0); +} + +using IoctlTestSIOCGIFCONF = SimpleSocketTest; + +TEST_P(IoctlTestSIOCGIFCONF, ValidateNoArrayGetsLength) { + auto fd = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + // Validate that no array can be used to get the length required. + struct ifconf ifconf = {}; + ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); + ASSERT_GT(ifconf.ifc_len, 0); +} + +// This test validates that we will only return a partial array list and not +// partial ifrreq structs. +TEST_P(IoctlTestSIOCGIFCONF, ValidateNoPartialIfrsReturned) { + auto fd = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + struct ifreq ifr = {}; + struct ifconf ifconf = {}; + ifconf.ifc_len = sizeof(ifr) - 1; // One byte too few. + ifconf.ifc_ifcu.ifcu_req = 𝔦 + + ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); + ASSERT_EQ(ifconf.ifc_len, 0); + ASSERT_EQ(ifr.ifr_name[0], '\0'); // Nothing is returned. + + ifconf.ifc_len = sizeof(ifreq); + ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); + ASSERT_GT(ifconf.ifc_len, 0); + ASSERT_NE(ifr.ifr_name[0], '\0'); // An interface can now be returned. +} + +TEST_P(IoctlTestSIOCGIFCONF, ValidateLoopbackIsPresent) { + auto fd = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + struct ifconf ifconf = {}; + struct ifreq ifr[10] = {}; // Storage for up to 10 interfaces. + + ifconf.ifc_req = ifr; + ifconf.ifc_len = sizeof(ifr); + + ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); + size_t num_if = ifconf.ifc_len / sizeof(struct ifreq); + + // We should have at least one interface. + ASSERT_GE(num_if, 1); + + // One of the interfaces should be a loopback. + bool found_loopback = false; + for (size_t i = 0; i < num_if; ++i) { + if (strcmp(ifr[i].ifr_name, "lo") == 0) { + // SIOCGIFCONF returns the ipv4 address of the interface, let's check it. + ASSERT_EQ(ifr[i].ifr_addr.sa_family, AF_INET); + + // Validate the address is correct for loopback. + sockaddr_in* sin = reinterpret_cast(&ifr[i].ifr_addr); + ASSERT_EQ(htonl(sin->sin_addr.s_addr), INADDR_LOOPBACK); + + found_loopback = true; + break; + } + } + ASSERT_TRUE(found_loopback); +} + +std::vector IoctlSocketTypes() { + return {SimpleSocket(AF_UNIX, SOCK_STREAM, 0), + SimpleSocket(AF_UNIX, SOCK_DGRAM, 0), + SimpleSocket(AF_INET, SOCK_STREAM, 0), + SimpleSocket(AF_INET6, SOCK_STREAM, 0), + SimpleSocket(AF_INET, SOCK_DGRAM, 0), + SimpleSocket(AF_INET6, SOCK_DGRAM, 0)}; +} + +INSTANTIATE_TEST_CASE_P(IoctlTest, IoctlTestSIOCGIFCONF, + ::testing::ValuesIn(IoctlSocketTypes())); + +} // namespace + +TEST_F(IoctlTest, FIOGETOWNSucceeds) { + const FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + int get = -1; + ASSERT_THAT(ioctl(s.get(), FIOGETOWN, &get), SyscallSucceeds()); + EXPECT_EQ(get, 0); +} + +TEST_F(IoctlTest, SIOCGPGRPSucceeds) { + const FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + + int get = -1; + ASSERT_THAT(ioctl(s.get(), SIOCGPGRP, &get), SyscallSucceeds()); + EXPECT_EQ(get, 0); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc new file mode 100644 index 000000000..1659d3d83 --- /dev/null +++ b/test/syscalls/linux/ip_socket_test_util.cc @@ -0,0 +1,78 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/ip_socket_test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +std::string DescribeSocketType(int type) { + return absl::StrCat(((type & SOCK_NONBLOCK) != 0) ? "non-blocking " : "", + ((type & SOCK_CLOEXEC) != 0) ? "close-on-exec " : ""); +} + +} // namespace + +SocketPairKind IPv6TCPAcceptBindSocketPair(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "IPv6 TCP socket"); + return SocketPairKind{ + description, TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM, + 0, /* dual_stack = */ false)}; +} + +SocketPairKind IPv4TCPAcceptBindSocketPair(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "IPv4 TCP socket"); + return SocketPairKind{ + description, TCPAcceptBindSocketPairCreator(AF_INET, type | SOCK_STREAM, + 0, /* dual_stack = */ false)}; +} + +SocketPairKind DualStackTCPAcceptBindSocketPair(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "dual stack TCP socket"); + return SocketPairKind{ + description, TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM, + 0, /* dual_stack = */ true)}; +} + +SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "IPv6 UDP socket"); + return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator( + AF_INET6, type | SOCK_DGRAM, 0, + /* dual_stack = */ false)}; +} + +SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket"); + return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator( + AF_INET, type | SOCK_DGRAM, 0, + /* dual_stack = */ false)}; +} + +SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "dual stack UDP socket"); + return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator( + AF_INET6, type | SOCK_DGRAM, 0, + /* dual_stack = */ true)}; +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h new file mode 100644 index 000000000..1e1400ecd --- /dev/null +++ b/test/syscalls/linux/ip_socket_test_util.h @@ -0,0 +1,57 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_ +#define GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_ + +#include +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// IPv6TCPAcceptBindSocketPair returns a SocketPairKind that represents +// SocketPairs created with bind() and accept() syscalls with AF_INET6 and the +// given type bound to the IPv6 loopback. +SocketPairKind IPv6TCPAcceptBindSocketPair(int type); + +// IPv4TCPAcceptBindSocketPair returns a SocketPairKind that represents +// SocketPairs created with bind() and accept() syscalls with AF_INET and the +// given type bound to the IPv4 loopback. +SocketPairKind IPv4TCPAcceptBindSocketPair(int type); + +// DualStackTCPAcceptBindSocketPair returns a SocketPairKind that represents +// SocketPairs created with bind() and accept() syscalls with AF_INET6 and the +// given type bound to the IPv4 loopback. +SocketPairKind DualStackTCPAcceptBindSocketPair(int type); + +// IPv6UDPBidirectionalBindSocketPair returns a SocketPairKind that represents +// SocketPairs created with bind() and connect() syscalls with AF_INET6 and the +// given type bound to the IPv6 loopback. +SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type); + +// IPv4UDPBidirectionalBindSocketPair returns a SocketPairKind that represents +// SocketPairs created with bind() and connect() syscalls with AF_INET and the +// given type bound to the IPv4 loopback. +SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type); + +// DualStackUDPBidirectionalBindSocketPair returns a SocketPairKind that +// represents SocketPairs created with bind() and connect() syscalls with +// AF_INET6 and the given type bound to the IPv4 loopback. +SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_ diff --git a/test/syscalls/linux/itimer.cc b/test/syscalls/linux/itimer.cc new file mode 100644 index 000000000..ee5871cbe --- /dev/null +++ b/test/syscalls/linux/itimer.cc @@ -0,0 +1,342 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { +namespace { + +constexpr char kSIGALRMToMainThread[] = "--itimer_sigarlm_to_main_thread"; +constexpr char kSIGPROFFairnessActive[] = "--itimer_sigprof_fairness_active"; +constexpr char kSIGPROFFairnessIdle[] = "--itimer_sigprof_fairness_idle"; + +// Time period to be set for the itimers. +constexpr absl::Duration kPeriod = absl::Milliseconds(25); +// Total amount of time to spend per thread. +constexpr absl::Duration kTestDuration = absl::Seconds(20); +// Amount of spin iterations to perform as the minimum work item per thread. +// Chosen to be sub-millisecond range. +constexpr int kIterations = 10000000; +// Allow deviation in the number of samples. +constexpr double kNumSamplesDeviationRatio = 0.2; +constexpr double kNumSamplesMinRatio = 0.5; + +TEST(ItimerTest, ItimervalUpdatedBeforeExpiration) { + constexpr int kSleepSecs = 10; + constexpr int kAlarmSecs = 15; + static_assert( + kSleepSecs < kAlarmSecs, + "kSleepSecs must be less than kAlarmSecs for the test to be meaningful"); + constexpr int kMaxRemainingSecs = kAlarmSecs - kSleepSecs; + + // Install a no-op handler for SIGALRM. + struct sigaction sa = {}; + sigfillset(&sa.sa_mask); + sa.sa_handler = +[](int signo) {}; + auto const cleanup_sa = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Set an itimer-based alarm for kAlarmSecs from now. + struct itimerval itv = {}; + itv.it_value.tv_sec = kAlarmSecs; + auto const cleanup_itimer = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, itv)); + + // After sleeping for kSleepSecs, the itimer value should reflect the elapsed + // time even if it hasn't expired. + absl::SleepFor(absl::Seconds(kSleepSecs)); + ASSERT_THAT(getitimer(ITIMER_REAL, &itv), SyscallSucceeds()); + EXPECT_TRUE( + itv.it_value.tv_sec < kMaxRemainingSecs || + (itv.it_value.tv_sec == kMaxRemainingSecs && itv.it_value.tv_usec == 0)) + << "Remaining time: " << itv.it_value.tv_sec << " seconds + " + << itv.it_value.tv_usec << " microseconds"; +} + +ABSL_CONST_INIT static thread_local std::atomic_int signal_test_num_samples = + ATOMIC_VAR_INIT(0); + +void SignalTestSignalHandler(int /*signum*/) { signal_test_num_samples++; } + +struct SignalTestResult { + int expected_total; + int main_thread_samples; + std::vector worker_samples; +}; + +std::ostream& operator<<(std::ostream& os, const SignalTestResult& r) { + os << "{expected_total: " << r.expected_total + << ", main_thread_samples: " << r.main_thread_samples + << ", worker_samples: ["; + bool first = true; + for (int sample : r.worker_samples) { + if (!first) { + os << ", "; + } + os << sample; + first = false; + } + os << "]}"; + return os; +} + +// Starts two worker threads and itimer id and measures the number of signal +// delivered to each thread. +SignalTestResult ItimerSignalTest(int id, clock_t main_clock, + clock_t worker_clock, int signal, + absl::Duration sleep) { + signal_test_num_samples = 0; + + struct sigaction sa = {}; + sa.sa_handler = &SignalTestSignalHandler; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + auto sigaction_cleanup = std::move(ScopedSigaction(signal, sa).ValueOrDie()); + + int socketfds[2]; + TEST_PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, socketfds) == 0); + + // Do the spinning in the workers. + std::function work = [&](int socket_fd) { + FileDescriptor fd(socket_fd); + + absl::Time finish = Now(worker_clock) + kTestDuration; + while (Now(worker_clock) < finish) { + // Blocked on read. + char c; + RetryEINTR(read)(fd.get(), &c, 1); + for (int i = 0; i < kIterations; i++) { + // Ensure compiler won't optimize this loop away. + asm(""); + } + + if (sleep != absl::ZeroDuration()) { + // Sleep so that the entire process is idle for a while. + absl::SleepFor(sleep); + } + + // Unblock the other thread. + RetryEINTR(write)(fd.get(), &c, 1); + } + + return reinterpret_cast(signal_test_num_samples.load()); + }; + + ScopedThread th1( + static_cast>(std::bind(work, socketfds[0]))); + ScopedThread th2( + static_cast>(std::bind(work, socketfds[1]))); + + absl::Time start = Now(main_clock); + // Start the timer. + struct itimerval timer = {}; + timer.it_value = absl::ToTimeval(kPeriod); + timer.it_interval = absl::ToTimeval(kPeriod); + auto cleanup_itimer = std::move(ScopedItimer(id, timer).ValueOrDie()); + + // Unblock th1. + // + // N.B. th2 owns socketfds[1] but can't close it until it unblocks. + char c = 0; + TEST_CHECK(write(socketfds[1], &c, 1) == 1); + + SignalTestResult result; + + // Wait for the workers to be done and collect their sample counts. + result.worker_samples.push_back(reinterpret_cast(th1.Join())); + result.worker_samples.push_back(reinterpret_cast(th2.Join())); + cleanup_itimer.Release()(); + result.expected_total = (Now(main_clock) - start) / kPeriod; + result.main_thread_samples = signal_test_num_samples.load(); + + return result; +} + +int TestSIGALRMToMainThread() { + SignalTestResult result = + ItimerSignalTest(ITIMER_REAL, CLOCK_REALTIME, CLOCK_REALTIME, SIGALRM, + absl::ZeroDuration()); + + std::cerr << "result: " << result << std::endl; + + // ITIMER_REAL-generated SIGALRMs prefer to deliver to the thread group leader + // (but don't guarantee it), so we expect to see most samples on the main + // thread. + // + // Linux only guarantees timers will never expire before the requested time. + // Thus, we only check the upper bound and also it at least have one sample. + TEST_CHECK(result.main_thread_samples <= result.expected_total); + TEST_CHECK(result.main_thread_samples > 0); + for (int num : result.worker_samples) { + TEST_CHECK_MSG(num <= 50, "worker received too many samples"); + } + + return 0; +} + +// Random save/restore is disabled as it introduces additional latency and +// unpredictable distribution patterns. +TEST(ItimerTest, DeliversSIGALRMToMainThread_NoRandomSave) { + pid_t child; + int execve_errno; + auto kill = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGALRMToMainThread}, + {}, &child, &execve_errno)); + EXPECT_EQ(0, execve_errno); + + int status; + EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + + // Not required anymore. + kill.Release(); + + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status; +} + +// Signals are delivered to threads fairly. +// +// sleep indicates how long to sleep worker threads each iteration to make the +// entire process idle. +int TestSIGPROFFairness(absl::Duration sleep) { + SignalTestResult result = + ItimerSignalTest(ITIMER_PROF, CLOCK_PROCESS_CPUTIME_ID, + CLOCK_THREAD_CPUTIME_ID, SIGPROF, sleep); + + std::cerr << "result: " << result << std::endl; + + // The number of samples on the main thread should be very low as it did + // nothing. + TEST_CHECK(result.main_thread_samples < 60); + + // Both workers should get roughly equal number of samples. + TEST_CHECK(result.worker_samples.size() == 2); + + TEST_CHECK(result.expected_total > 0); + + // In an ideal world each thread would get exactly 50% of the signals, + // but since that's unlikely to happen we allow for them to get no less than + // kNumSamplesDeviationRatio of the total observed samples. + TEST_CHECK_MSG(std::abs(result.worker_samples[0] - result.worker_samples[1]) < + ((result.worker_samples[0] + result.worker_samples[1]) * + kNumSamplesDeviationRatio), + "one worker received disproportionate share of samples"); + + return 0; +} + +// Random save/restore is disabled as it introduces additional latency and +// unpredictable distribution patterns. +TEST(ItimerTest, DeliversSIGPROFToThreadsRoughlyFairlyActive_NoRandomSave) { + pid_t child; + int execve_errno; + auto kill = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGPROFFairnessActive}, + {}, &child, &execve_errno)); + EXPECT_EQ(0, execve_errno); + + int status; + EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + + // Not required anymore. + kill.Release(); + + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +// Random save/restore is disabled as it introduces additional latency and +// unpredictable distribution patterns. +TEST(ItimerTest, DeliversSIGPROFToThreadsRoughlyFairlyIdle_NoRandomSave) { + pid_t child; + int execve_errno; + auto kill = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGPROFFairnessIdle}, + {}, &child, &execve_errno)); + EXPECT_EQ(0, execve_errno); + + int status; + EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + + // Not required anymore. + kill.Release(); + + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "Exited with code: " << status; +} + +} // namespace +} // namespace testing +} // namespace gvisor + +namespace { +void MaskSIGPIPE() { + // Always mask SIGPIPE as it's common and tests aren't expected to handle it. + // We don't take the TestInit() path so we must do this manually. + struct sigaction sa = {}; + sa.sa_handler = SIG_IGN; + TEST_CHECK(sigaction(SIGPIPE, &sa, nullptr) == 0); +} +} // namespace + +int main(int argc, char** argv) { + // These tests require no background threads, so check for them before + // TestInit. + for (int i = 0; i < argc; i++) { + absl::string_view arg(argv[i]); + + if (arg == gvisor::testing::kSIGALRMToMainThread) { + MaskSIGPIPE(); + return gvisor::testing::TestSIGALRMToMainThread(); + } + if (arg == gvisor::testing::kSIGPROFFairnessActive) { + MaskSIGPIPE(); + return gvisor::testing::TestSIGPROFFairness(absl::ZeroDuration()); + } + if (arg == gvisor::testing::kSIGPROFFairnessIdle) { + MaskSIGPIPE(); + return gvisor::testing::TestSIGPROFFairness(absl::Milliseconds(10)); + } + } + + gvisor::testing::TestInit(&argc, &argv); + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/kill.cc b/test/syscalls/linux/kill.cc new file mode 100644 index 000000000..18ba8fb16 --- /dev/null +++ b/test/syscalls/linux/kill.cc @@ -0,0 +1,380 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_int32(scratch_uid, 65534, "scratch UID"); +DEFINE_int32(scratch_gid, 65534, "scratch GID"); + +using ::testing::Ge; + +namespace gvisor { +namespace testing { + +namespace { + +TEST(KillTest, CanKillValidPid) { + // If pid is positive, then signal sig is sent to the process with the ID + // specified by pid. + EXPECT_THAT(kill(getpid(), 0), SyscallSucceeds()); + // If pid equals 0, then sig is sent to every process in the process group of + // the calling process. + EXPECT_THAT(kill(0, 0), SyscallSucceeds()); + + ScopedThread([] { EXPECT_THAT(kill(gettid(), 0), SyscallSucceeds()); }); +} + +void SigHandler(int sig, siginfo_t* info, void* context) { _exit(0); } + +// If pid equals -1, then sig is sent to every process for which the calling +// process has permission to send signals, except for process 1 (init). +TEST(KillTest, CanKillAllPIDs) { + int pipe_fds[2]; + ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); + FileDescriptor read_fd(pipe_fds[0]); + FileDescriptor write_fd(pipe_fds[1]); + + pid_t pid = fork(); + if (pid == 0) { + read_fd.reset(); + + struct sigaction sa; + sa.sa_sigaction = SigHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGWINCH, sa)); + + // Indicate to the parent that we're ready. + write_fd.reset(); + + // Wait until we get the signal from the parent. + while (true) { + pause(); + } + } + + EXPECT_THAT(pid, SyscallSucceeds()); + + write_fd.reset(); + + // Wait for the child to indicate that it's unmasked the signal by closing + // the write end. + char buf; + ASSERT_THAT(ReadFd(read_fd.get(), &buf, 1), SyscallSucceedsWithValue(0)); + + // Signal the child and wait for it to die with status 0, indicating that + // it got the expected signal. + EXPECT_THAT(kill(-1, SIGWINCH), SyscallSucceeds()); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), + SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); +} + +TEST(KillTest, CannotKillInvalidPID) { + // We need an unused pid to verify that kill fails when given one. + // + // There is no way to guarantee that a PID is unused, but the PID of a + // recently exited process likely won't be reused soon. + pid_t fake_pid = fork(); + if (fake_pid == 0) { + _exit(0); + } + + EXPECT_THAT(fake_pid, SyscallSucceeds()); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(fake_pid, &status, 0), + SyscallSucceedsWithValue(fake_pid)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); + + EXPECT_THAT(kill(fake_pid, 0), SyscallFailsWithErrno(ESRCH)); +} + +TEST(KillTest, CannotUseInvalidSignal) { + EXPECT_THAT(kill(getpid(), 200), SyscallFailsWithErrno(EINVAL)); +} + +TEST(KillTest, CanKillRemoteProcess) { + pid_t pid = fork(); + if (pid == 0) { + while (true) { + pause(); + } + } + + EXPECT_THAT(pid, SyscallSucceeds()); + + EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds()); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), + SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(SIGKILL, WTERMSIG(status)); +} + +TEST(KillTest, CanKillOwnProcess) { + EXPECT_THAT(kill(getpid(), 0), SyscallSucceeds()); +} + +// Verify that you can kill a process even using a tid from a thread other than +// the group leader. +TEST(KillTest, CannotKillTid) { + pid_t tid; + bool tid_available = false; + bool finished = false; + absl::Mutex mu; + ScopedThread t([&] { + mu.Lock(); + tid = gettid(); + tid_available = true; + mu.Await(absl::Condition(&finished)); + mu.Unlock(); + }); + mu.LockWhen(absl::Condition(&tid_available)); + EXPECT_THAT(kill(tid, 0), SyscallSucceeds()); + finished = true; + mu.Unlock(); +} + +TEST(KillTest, SetPgid) { + for (int i = 0; i < 10; i++) { + // The following in the normal pattern for creating a new process group. + // Both the parent and child process will call setpgid in order to avoid any + // race conditions. We do this ten times to catch races. + pid_t pid = fork(); + if (pid == 0) { + setpgid(0, 0); + while (true) { + pause(); + } + } + + EXPECT_THAT(pid, SyscallSucceeds()); + + // Set the child's group and exit. + ASSERT_THAT(setpgid(pid, pid), SyscallSucceeds()); + EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds()); + + int status; + EXPECT_THAT(RetryEINTR(waitpid)(-pid, &status, 0), + SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(SIGKILL, WTERMSIG(status)); + } +} + +TEST(KillTest, ProcessGroups) { + // Fork a new child. + // + // other_child is used as a placeholder process. We use this PID as our "does + // not exist" process group to ensure some amount of safety. (It is still + // possible to violate this assumption, but extremely unlikely.) + pid_t child = fork(); + if (child == 0) { + while (true) { + pause(); + } + } + EXPECT_THAT(child, SyscallSucceeds()); + + pid_t other_child = fork(); + if (other_child == 0) { + while (true) { + pause(); + } + } + + // Ensure the kill does not succeed without the new group. + EXPECT_THAT(kill(-child, SIGKILL), SyscallFailsWithErrno(ESRCH)); + + // Put the child in its own process group. + ASSERT_THAT(setpgid(child, child), SyscallSucceeds()); + + // This should be not allowed: you can only create a new group with the same + // id or join an existing one. The other_child group should not exist. + ASSERT_THAT(setpgid(child, other_child), SyscallFailsWithErrno(EPERM)); + + // Done with other_child; kill it. + EXPECT_THAT(kill(other_child, SIGKILL), SyscallSucceeds()); + int status; + EXPECT_THAT(RetryEINTR(waitpid)(other_child, &status, 0), SyscallSucceeds()); + + // Linux returns success for the no-op call. + ASSERT_THAT(setpgid(child, child), SyscallSucceeds()); + + // Kill the child's process group. + ASSERT_THAT(kill(-child, SIGKILL), SyscallSucceeds()); + + // Wait on the process group; ensure that the signal was as expected. + EXPECT_THAT(RetryEINTR(waitpid)(-child, &status, 0), + SyscallSucceedsWithValue(child)); + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(SIGKILL, WTERMSIG(status)); + + // Try to kill the process group again; ensure that the wait fails. + EXPECT_THAT(kill(-child, SIGKILL), SyscallFailsWithErrno(ESRCH)); + EXPECT_THAT(RetryEINTR(waitpid)(-child, &status, 0), + SyscallFailsWithErrno(ECHILD)); +} + +TEST(KillTest, ChildDropsPrivsCannotKill) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + + int uid = FLAGS_scratch_uid; + int gid = FLAGS_scratch_gid; + + // Create the child that drops privileges and tries to kill the parent. + pid_t pid = fork(); + if (pid == 0) { + TEST_PCHECK(setresgid(gid, gid, gid) == 0); + MaybeSave(); + + TEST_PCHECK(setresuid(uid, uid, uid) == 0); + MaybeSave(); + + // setresuid should have dropped CAP_KILL. Make sure. + TEST_CHECK(!HaveCapability(CAP_KILL).ValueOrDie()); + + // Try to kill parent with every signal-sending syscall possible. + pid_t parent = getppid(); + + TEST_CHECK(kill(parent, SIGKILL) < 0); + TEST_PCHECK_MSG(errno == EPERM, "kill failed with wrong errno"); + MaybeSave(); + + TEST_CHECK(tgkill(parent, parent, SIGKILL) < 0); + TEST_PCHECK_MSG(errno == EPERM, "tgkill failed with wrong errno"); + MaybeSave(); + + TEST_CHECK(syscall(SYS_tkill, parent, SIGKILL) < 0); + TEST_PCHECK_MSG(errno == EPERM, "tkill failed with wrong errno"); + MaybeSave(); + + siginfo_t uinfo; + uinfo.si_code = -1; // SI_QUEUE (allowed). + + TEST_CHECK(syscall(SYS_rt_sigqueueinfo, parent, SIGKILL, &uinfo) < 0); + TEST_PCHECK_MSG(errno == EPERM, "rt_sigqueueinfo failed with wrong errno"); + MaybeSave(); + + TEST_CHECK(syscall(SYS_rt_tgsigqueueinfo, parent, parent, SIGKILL, &uinfo) < + 0); + TEST_PCHECK_MSG(errno == EPERM, "rt_sigqueueinfo failed with wrong errno"); + MaybeSave(); + + _exit(0); + } + + EXPECT_THAT(pid, SyscallSucceeds()); + + int status; + EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), + SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status = " << status; +} + +TEST(KillTest, CanSIGCONTSameSession) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + + pid_t stopped_child = fork(); + if (stopped_child == 0) { + raise(SIGSTOP); + _exit(0); + } + + EXPECT_THAT(stopped_child, SyscallSucceeds()); + + // Put the child in its own process group. The child and parent process + // groups also share a session. + ASSERT_THAT(setpgid(stopped_child, stopped_child), SyscallSucceeds()); + + // Make sure child stopped. + int status; + EXPECT_THAT(RetryEINTR(waitpid)(stopped_child, &status, WUNTRACED), + SyscallSucceedsWithValue(stopped_child)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << "status " << status; + + int uid = FLAGS_scratch_uid; + int gid = FLAGS_scratch_gid; + + // Drop privileges only in child process, or else this parent process won't be + // able to open some log files after the test ends. + pid_t other_child = fork(); + if (other_child == 0) { + // Drop privileges. + TEST_PCHECK(setresgid(gid, gid, gid) == 0); + MaybeSave(); + + TEST_PCHECK(setresuid(uid, uid, uid) == 0); + MaybeSave(); + + // setresuid should have dropped CAP_KILL. + TEST_CHECK(!HaveCapability(CAP_KILL).ValueOrDie()); + + // Child 2 and child should now not share a thread group and any UIDs. + // Child 2 should have no privileges. That means any signal other than + // SIGCONT should fail. + TEST_CHECK(kill(stopped_child, SIGKILL) < 0); + TEST_PCHECK_MSG(errno == EPERM, "kill failed with wrong errno"); + MaybeSave(); + + TEST_PCHECK(kill(stopped_child, SIGCONT) == 0); + MaybeSave(); + + _exit(0); + } + + EXPECT_THAT(stopped_child, SyscallSucceeds()); + + // Make sure child exited normally. + EXPECT_THAT(RetryEINTR(waitpid)(stopped_child, &status, 0), + SyscallSucceedsWithValue(stopped_child)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; + + // Make sure other_child exited normally. + EXPECT_THAT(RetryEINTR(waitpid)(other_child, &status, 0), + SyscallSucceedsWithValue(other_child)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc new file mode 100644 index 000000000..ed74437bc --- /dev/null +++ b/test/syscalls/linux/link.cc @@ -0,0 +1,291 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_int32(scratch_uid, 65534, "scratch UID"); + +namespace gvisor { +namespace testing { + +namespace { + +// IsSameFile returns true if both filenames have the same device and inode. +bool IsSameFile(const std::string& f1, const std::string& f2) { + // Use lstat rather than stat, so that symlinks are not followed. + struct stat stat1 = {}; + EXPECT_THAT(lstat(f1.c_str(), &stat1), SyscallSucceeds()); + struct stat stat2 = {}; + EXPECT_THAT(lstat(f2.c_str(), &stat2), SyscallSucceeds()); + + return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino; +} + +TEST(LinkTest, CanCreateLinkFile) { + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string newname = NewTempAbsPath(); + + // Get the initial link count. + uint64_t initial_link_count = ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path())); + + EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), SyscallSucceeds()); + + EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); + + // Link count should be incremented. + EXPECT_THAT(Links(oldfile.path()), + IsPosixErrorOkAndHolds(initial_link_count + 1)); + + // Delete the link. + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); + + // Link count should be back to initial. + EXPECT_THAT(Links(oldfile.path()), + IsPosixErrorOkAndHolds(initial_link_count)); +} + +TEST(LinkTest, PermissionDenied) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_FOWNER))); + + // Make the file "unsafe" to link by making it only readable, but not + // writable. + const auto oldfile = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400)); + const std::string newname = NewTempAbsPath(); + + // Do setuid in a separate thread so that after finishing this test, the + // process can still open files the test harness created before starting this + // test. Otherwise, the files are created by root (UID before the test), but + // cannot be opened by the `uid` set below after the test. After calling + // setuid(non-zero-UID), there is no way to get root privileges back. + ScopedThread([&] { + // Use syscall instead of glibc setuid wrapper because we want this setuid + // call to only apply to this task. POSIX threads, however, require that all + // threads have the same UIDs, so using the setuid wrapper sets all threads' + // real UID. + // Also drops capabilities. + EXPECT_THAT(syscall(SYS_setuid, FLAGS_scratch_uid), SyscallSucceeds()); + + EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), + SyscallFailsWithErrno(EPERM)); + }); +} + +TEST(LinkTest, CannotLinkDirectory) { + auto olddir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string newdir = NewTempAbsPath(); + + EXPECT_THAT(link(olddir.path().c_str(), newdir.c_str()), + SyscallFailsWithErrno(EPERM)); + + EXPECT_THAT(rmdir(olddir.path().c_str()), SyscallSucceeds()); +} + +TEST(LinkTest, CannotLinkWithSlash) { + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + // Put a final "/" on newname. + const std::string newname = absl::StrCat(NewTempAbsPath(), "/"); + + EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), + SyscallFailsWithErrno(ENOENT)); +} + +TEST(LinkTest, OldnameIsEmpty) { + const std::string newname = NewTempAbsPath(); + EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); +} + +TEST(LinkTest, OldnameDoesNotExist) { + const std::string oldname = NewTempAbsPath(); + const std::string newname = NewTempAbsPath(); + EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); +} + +TEST(LinkTest, NewnameCannotExist) { + const std::string newname = + JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo"); + EXPECT_THAT(link("/thisdoesnotmatter", newname.c_str()), + SyscallFailsWithErrno(ENOENT)); +} + +TEST(LinkTest, WithOldDirFD) { + const std::string oldname_parent = NewTempAbsPath(); + const std::string oldname_base = "child"; + const std::string oldname = JoinPath(oldname_parent, oldname_base); + const std::string newname = NewTempAbsPath(); + + // Create oldname_parent directory, and get an FD. + ASSERT_THAT(mkdir(oldname_parent.c_str(), 0777), SyscallSucceeds()); + const FileDescriptor oldname_parent_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(oldname_parent, O_DIRECTORY | O_RDONLY)); + + // Create oldname file. + const FileDescriptor oldname_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(oldname, O_CREAT | O_RDWR, 0666)); + + // Link oldname to newname, using oldname_parent_fd. + EXPECT_THAT(linkat(oldname_parent_fd.get(), oldname_base.c_str(), AT_FDCWD, + newname.c_str(), 0), + SyscallSucceeds()); + + EXPECT_TRUE(IsSameFile(oldname, newname)); + + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); + EXPECT_THAT(rmdir(oldname_parent.c_str()), SyscallSucceeds()); +} + +TEST(LinkTest, BogusFlags) { + ASSERT_THAT(linkat(1, "foo", 2, "bar", 3), SyscallFailsWithErrno(EINVAL)); +} + +TEST(LinkTest, WithNewDirFD) { + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string newname_parent = NewTempAbsPath(); + const std::string newname_base = "child"; + const std::string newname = JoinPath(newname_parent, newname_base); + + // Create newname_parent directory, and get an FD. + EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds()); + const FileDescriptor newname_parent_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_RDONLY)); + + // Link newname to oldfile, using newname_parent_fd. + EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(), + newname.c_str(), 0), + SyscallSucceeds()); + + EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); + + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); + EXPECT_THAT(rmdir(newname_parent.c_str()), SyscallSucceeds()); +} + +TEST(LinkTest, RelPathsWithNonDirFDs) { + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Create a file that will be passed as the directory fd for old/new names. + const std::string filename = NewTempAbsPath(); + const FileDescriptor file_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666)); + + // Using file_fd as olddirfd will fail. + EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0), + SyscallFailsWithErrno(ENOTDIR)); + + // Using file_fd as newdirfd will fail. + EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0), + SyscallFailsWithErrno(ENOTDIR)); +} + +TEST(LinkTest, AbsPathsWithNonDirFDs) { + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string newname = NewTempAbsPath(); + + // Create a file that will be passed as the directory fd for old/new names. + const std::string filename = NewTempAbsPath(); + const FileDescriptor file_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666)); + + // Using file_fd as the dirfds is OK as long as paths are absolute. + EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(), + newname.c_str(), 0), + SyscallSucceeds()); +} + +TEST(LinkTest, LinkDoesNotFollowSymlinks) { + // Create oldfile, and oldsymlink which points to it. + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string oldsymlink = NewTempAbsPath(); + EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), + SyscallSucceeds()); + + // Now hard link newname to oldsymlink. + const std::string newname = NewTempAbsPath(); + EXPECT_THAT(link(oldsymlink.c_str(), newname.c_str()), SyscallSucceeds()); + + // The link should not have resolved the symlink, so newname and oldsymlink + // are the same. + EXPECT_TRUE(IsSameFile(oldsymlink, newname)); + EXPECT_FALSE(IsSameFile(oldfile.path(), newname)); + + EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); +} + +TEST(LinkTest, LinkatDoesNotFollowSymlinkByDefault) { + // Create oldfile, and oldsymlink which points to it. + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string oldsymlink = NewTempAbsPath(); + EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), + SyscallSucceeds()); + + // Now hard link newname to oldsymlink. + const std::string newname = NewTempAbsPath(); + EXPECT_THAT( + linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), 0), + SyscallSucceeds()); + + // The link should not have resolved the symlink, so newname and oldsymlink + // are the same. + EXPECT_TRUE(IsSameFile(oldsymlink, newname)); + EXPECT_FALSE(IsSameFile(oldfile.path(), newname)); + + EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); +} + +TEST(LinkTest, LinkatWithSymlinkFollow) { + // Create oldfile, and oldsymlink which points to it. + auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string oldsymlink = NewTempAbsPath(); + ASSERT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), + SyscallSucceeds()); + + // Now hard link newname to oldsymlink, and pass AT_SYMLINK_FOLLOW flag. + const std::string newname = NewTempAbsPath(); + ASSERT_THAT(linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), + AT_SYMLINK_FOLLOW), + SyscallSucceeds()); + + // The link should have resolved the symlink, so oldfile and newname are the + // same. + EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); + EXPECT_FALSE(IsSameFile(oldsymlink, newname)); + + EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/lseek.cc b/test/syscalls/linux/lseek.cc new file mode 100644 index 000000000..fb6a1546e --- /dev/null +++ b/test/syscalls/linux/lseek.cc @@ -0,0 +1,202 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(LseekTest, InvalidWhence) { + const std::string kFileData = "hello world\n"; + const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); + + ASSERT_THAT(lseek(fd.get(), 0, -1), SyscallFailsWithErrno(EINVAL)); +} + +TEST(LseekTest, NegativeOffset) { + const std::string kFileData = "hello world\n"; + const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); + + EXPECT_THAT(lseek(fd.get(), -(kFileData.length() + 1), SEEK_CUR), + SyscallFailsWithErrno(EINVAL)); +} + +// A 32-bit off_t is not large enough to represent an offset larger than +// maximum file size on standard file systems, so it isn't possible to cause +// overflow. +#ifdef __x86_64__ +TEST(LseekTest, Overflow) { + // HA! Classic Linux. We really should have an EOVERFLOW + // here, since we're seeking to something that cannot be + // represented.. but instead we are given an EINVAL. + const std::string kFileData = "hello world\n"; + const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); + EXPECT_THAT(lseek(fd.get(), 0x7fffffffffffffff, SEEK_END), + SyscallFailsWithErrno(EINVAL)); +} +#endif + +TEST(LseekTest, Set) { + const std::string kFileData = "hello world\n"; + const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); + + char buf = '\0'; + EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ(buf, kFileData.c_str()[0]); + EXPECT_THAT(lseek(fd.get(), 6, SEEK_SET), SyscallSucceedsWithValue(6)); + ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ(buf, kFileData.c_str()[6]); +} + +TEST(LseekTest, Cur) { + const std::string kFileData = "hello world\n"; + const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); + + char buf = '\0'; + EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ(buf, kFileData.c_str()[0]); + EXPECT_THAT(lseek(fd.get(), 3, SEEK_CUR), SyscallSucceedsWithValue(4)); + ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ(buf, kFileData.c_str()[4]); +} + +TEST(LseekTest, End) { + const std::string kFileData = "hello world\n"; + const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); + + char buf = '\0'; + EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ(buf, kFileData.c_str()[0]); + EXPECT_THAT(lseek(fd.get(), -2, SEEK_END), SyscallSucceedsWithValue(10)); + ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ(buf, kFileData.c_str()[kFileData.length() - 2]); +} + +TEST(LseekTest, InvalidFD) { + EXPECT_THAT(lseek(-1, 0, SEEK_SET), SyscallFailsWithErrno(EBADF)); +} + +TEST(LseekTest, DirCurEnd) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/tmp", O_RDONLY)); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); +} + +TEST(LseekTest, ProcDir) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self", O_RDONLY)); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); +} + +TEST(LseekTest, ProcFile) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/meminfo", O_RDONLY)); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); +} + +TEST(LseekTest, SysDir) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/sys/devices", O_RDONLY)); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); +} + +TEST(LseekTest, SeekCurrentDir) { + // From include/linux/fs.h. + constexpr loff_t MAX_LFS_FILESIZE = 0x7fffffffffffffff; + + char* dir = get_current_dir_name(); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir, O_RDONLY)); + + ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); + ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), + // Some filesystems (like ext4) allow lseek(SEEK_END) on a + // directory and return MAX_LFS_FILESIZE, others return EINVAL. + AnyOf(SyscallSucceedsWithValue(MAX_LFS_FILESIZE), + SyscallFailsWithErrno(EINVAL))); + free(dir); +} + +TEST(LseekTest, ProcStatTwice) { + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); + const FileDescriptor fd2 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); + + ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + ASSERT_THAT(lseek(fd1.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); + // Check that just because we moved fd1, fd2 doesn't move. + ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + + const FileDescriptor fd3 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); + ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); +} + +TEST(LseekTest, EtcPasswdDup) { + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/etc/passwd", O_RDONLY)); + const FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); + + ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); + // Check that just because we moved fd1, fd2 doesn't move. + ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); + + const FileDescriptor fd3 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); + ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); +} + +// TODO: Add tests where we have donated in sockets. + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/madvise.cc b/test/syscalls/linux/madvise.cc new file mode 100644 index 000000000..a79c8c75d --- /dev/null +++ b/test/syscalls/linux/madvise.cc @@ -0,0 +1,142 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +void ExpectAllMappingBytes(Mapping const& m, char c) { + auto const v = m.view(); + for (size_t i = 0; i < kPageSize; i++) { + ASSERT_EQ(v[i], c) << "at offset " << i; + } +} + +// Equivalent to ExpectAllMappingBytes but async-signal-safe and with less +// helpful failure messages. +void CheckAllMappingBytes(Mapping const& m, char c) { + auto const v = m.view(); + for (size_t i = 0; i < kPageSize; i++) { + TEST_CHECK_MSG(v[i] == c, "mapping contains wrong value"); + } +} + +TEST(MadviseDontneedTest, ZerosPrivateAnonPage) { + auto m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + ExpectAllMappingBytes(m, 0); + memset(m.ptr(), 1, m.len()); + ExpectAllMappingBytes(m, 1); + ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); + ExpectAllMappingBytes(m, 0); +} + +TEST(MadviseDontneedTest, ZerosCOWAnonPageInCallerOnly) { + auto m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + ExpectAllMappingBytes(m, 0); + memset(m.ptr(), 2, m.len()); + ExpectAllMappingBytes(m, 2); + + // Do madvise in a child process. + pid_t pid = fork(); + CheckAllMappingBytes(m, 2); + if (pid == 0) { + TEST_PCHECK(madvise(m.ptr(), m.len(), MADV_DONTNEED) == 0); + CheckAllMappingBytes(m, 0); + _exit(0); + } + + ASSERT_THAT(pid, SyscallSucceeds()); + + int status = 0; + ASSERT_THAT(waitpid(-1, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(WEXITSTATUS(status), 0); + // The child's madvise should not have affected the parent. + ExpectAllMappingBytes(m, 2); +} + +TEST(MadviseDontneedTest, DoesNotModifySharedAnonPage) { + auto m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED)); + ExpectAllMappingBytes(m, 0); + memset(m.ptr(), 3, m.len()); + ExpectAllMappingBytes(m, 3); + ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); + ExpectAllMappingBytes(m, 3); +} + +TEST(MadviseDontneedTest, CleansPrivateFilePage) { + TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + /* parent = */ GetAbsoluteTestTmpdir(), + /* content = */ std::string(kPageSize, 4), TempPath::kDefaultFileMode)); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); + + Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( + nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd.get(), 0)); + ExpectAllMappingBytes(m, 4); + memset(m.ptr(), 5, m.len()); + ExpectAllMappingBytes(m, 5); + ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); + ExpectAllMappingBytes(m, 4); +} + +TEST(MadviseDontneedTest, DoesNotModifySharedFilePage) { + TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + /* parent = */ GetAbsoluteTestTmpdir(), + /* content = */ std::string(kPageSize, 6), TempPath::kDefaultFileMode)); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); + + Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( + nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); + ExpectAllMappingBytes(m, 6); + memset(m.ptr(), 7, m.len()); + ExpectAllMappingBytes(m, 7); + ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); + ExpectAllMappingBytes(m, 7); +} + +TEST(MadviseDontneedTest, IgnoresPermissions) { + auto m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); + EXPECT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/memory_accounting.cc b/test/syscalls/linux/memory_accounting.cc new file mode 100644 index 000000000..b4b680c34 --- /dev/null +++ b/test/syscalls/linux/memory_accounting.cc @@ -0,0 +1,99 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +using ::absl::StrFormat; + +// AnonUsageFromMeminfo scrapes the current anonymous memory usage from +// /proc/meminfo and returns it in bytes. +PosixErrorOr AnonUsageFromMeminfo() { + ASSIGN_OR_RETURN_ERRNO(auto meminfo, GetContents("/proc/meminfo")); + std::vector lines(absl::StrSplit(meminfo, '\n')); + + // Try to find AnonPages line, the format is AnonPages:\\s+(\\d+) kB\n. + for (const auto& line : lines) { + if (!absl::StartsWith(line, "AnonPages:")) { + continue; + } + + std::vector parts( + absl::StrSplit(line, ' ', absl::SkipEmpty())); + if (parts.size() == 3) { + // The size is the second field, let's try to parse it as a number. + ASSIGN_OR_RETURN_ERRNO(auto anon_kb, Atoi(parts[1])); + return anon_kb * 1024; + } + + return PosixError(EINVAL, "AnonPages field in /proc/meminfo was malformed"); + } + + return PosixError(EINVAL, "AnonPages field not found in /proc/meminfo"); +} + +TEST(MemoryAccounting, AnonAccountingPreservedOnSaveRestore) { + // This test isn't meaningful on Linux. /proc/meminfo reports system-wide + // memory usage, which can change arbitrarily in Linux from other activity on + // the machine. In gvisor, this test is the only thing running on the + // "machine", so values in /proc/meminfo accurately reflect the memory used by + // the test. + SKIP_IF(!IsRunningOnGvisor()); + + uint64_t anon_initial = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); + + // Cause some anonymous memory usage. + uint64_t map_bytes = Megabytes(512); + char* mem = + static_cast(mmap(nullptr, map_bytes, PROT_READ | PROT_WRITE, + MAP_POPULATE | MAP_ANON | MAP_PRIVATE, -1, 0)); + ASSERT_NE(mem, MAP_FAILED) + << "Map failed, errno: " << errno << " (" << strerror(errno) << ")."; + + // Write something to each page to prevent them from being decommited on + // S/R. Zero pages are dropped on save. + for (uint64_t i = 0; i < map_bytes; i += kPageSize) { + mem[i] = 'a'; + } + + uint64_t anon_after_alloc = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); + EXPECT_THAT(anon_after_alloc, + EquivalentWithin(anon_initial + map_bytes, 0.03)); + + // We have many implicit S/R cycles from scraping /proc/meminfo throughout the + // test, but throw an explicit S/R in here as well. + MaybeSave(); + + // Usage should remain the same across S/R. + uint64_t anon_after_sr = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); + EXPECT_THAT(anon_after_sr, EquivalentWithin(anon_after_alloc, 0.03)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc new file mode 100644 index 000000000..9f8033bdf --- /dev/null +++ b/test/syscalls/linux/mempolicy.cc @@ -0,0 +1,258 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "test/util/cleanup.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +#define BITS_PER_BYTE 8 + +#define MPOL_F_STATIC_NODES (1 << 15) +#define MPOL_F_RELATIVE_NODES (1 << 14) +#define MPOL_DEFAULT 0 +#define MPOL_PREFERRED 1 +#define MPOL_BIND 2 +#define MPOL_INTERLEAVE 3 +#define MPOL_MAX MPOL_INTERLEAVE +#define MPOL_F_NODE (1 << 0) +#define MPOL_F_ADDR (1 << 1) +#define MPOL_F_MEMS_ALLOWED (1 << 2) +#define MPOL_MF_STRICT (1 << 0) +#define MPOL_MF_MOVE (1 << 1) +#define MPOL_MF_MOVE_ALL (1 << 2) + +int get_mempolicy(int *policy, uint64_t *nmask, uint64_t maxnode, void *addr, + int flags) { + return syscall(__NR_get_mempolicy, policy, nmask, maxnode, addr, flags); +} + +int set_mempolicy(int mode, uint64_t *nmask, uint64_t maxnode) { + return syscall(__NR_set_mempolicy, mode, nmask, maxnode); +} + +// Creates a cleanup object that resets the calling thread's mempolicy to the +// system default when the calling scope ends. +Cleanup ScopedMempolicy() { + return Cleanup([] { + EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, nullptr, 0), SyscallSucceeds()); + }); +} + +// Temporarily change the memory policy for the calling thread within the +// caller's scope. +PosixErrorOr ScopedSetMempolicy(int mode, uint64_t *nmask, + uint64_t maxnode) { + if (set_mempolicy(mode, nmask, maxnode)) { + return PosixError(errno, "set_mempolicy"); + } + return ScopedMempolicy(); +} + +TEST(MempolicyTest, CheckDefaultPolicy) { + int mode = 0; + uint64_t nodemask = 0; + ASSERT_THAT(get_mempolicy(&mode, &nodemask, sizeof(nodemask) * BITS_PER_BYTE, + nullptr, 0), + SyscallSucceeds()); + + EXPECT_EQ(MPOL_DEFAULT, mode); + EXPECT_EQ(0x0, nodemask); +} + +TEST(MempolicyTest, PolicyPreservedAfterSetMempolicy) { + uint64_t nodemask = 0x1; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( + MPOL_BIND, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); + + int mode = 0; + uint64_t nodemask_after = 0x0; + ASSERT_THAT(get_mempolicy(&mode, &nodemask_after, + sizeof(nodemask_after) * BITS_PER_BYTE, nullptr, 0), + SyscallSucceeds()); + EXPECT_EQ(MPOL_BIND, mode); + EXPECT_EQ(0x1, nodemask_after); + + // Try throw in some mode flags. + for (auto mode_flag : {MPOL_F_STATIC_NODES, MPOL_F_RELATIVE_NODES}) { + auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( + ScopedSetMempolicy(MPOL_INTERLEAVE | mode_flag, &nodemask, + sizeof(nodemask) * BITS_PER_BYTE)); + mode = 0; + nodemask_after = 0x0; + ASSERT_THAT( + get_mempolicy(&mode, &nodemask_after, + sizeof(nodemask_after) * BITS_PER_BYTE, nullptr, 0), + SyscallSucceeds()); + EXPECT_EQ(MPOL_INTERLEAVE | mode_flag, mode); + EXPECT_EQ(0x1, nodemask_after); + } +} + +TEST(MempolicyTest, SetMempolicyRejectsInvalidInputs) { + auto cleanup = ScopedMempolicy(); + uint64_t nodemask; + + if (IsRunningOnGvisor()) { + // Invalid nodemask, we only support a single node on gvisor. + nodemask = 0x4; + ASSERT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, + sizeof(nodemask) * BITS_PER_BYTE), + SyscallFailsWithErrno(EINVAL)); + } + + nodemask = 0x1; + + // Invalid mode. + ASSERT_THAT(set_mempolicy(7439, &nodemask, sizeof(nodemask) * BITS_PER_BYTE), + SyscallFailsWithErrno(EINVAL)); + + // Invalid nodemask size. + ASSERT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, 0), + SyscallFailsWithErrno(EINVAL)); + + // Invalid mode flag. + ASSERT_THAT( + set_mempolicy(MPOL_DEFAULT | MPOL_F_STATIC_NODES | MPOL_F_RELATIVE_NODES, + &nodemask, sizeof(nodemask) * BITS_PER_BYTE), + SyscallFailsWithErrno(EINVAL)); + + // MPOL_INTERLEAVE with empty nodemask. + nodemask = 0x0; + ASSERT_THAT(set_mempolicy(MPOL_INTERLEAVE, &nodemask, + sizeof(nodemask) * BITS_PER_BYTE), + SyscallFailsWithErrno(EINVAL)); +} + +// The manpages specify that the nodemask provided to set_mempolicy are +// considered empty if the nodemask pointer is null, or if the nodemask size is +// 0. We use a policy which accepts both empty and non-empty nodemasks +// (MPOL_PREFERRED), a policy which requires a non-empty nodemask (MPOL_BIND), +// and a policy which completely ignores the nodemask (MPOL_DEFAULT) to verify +// argument checking around nodemasks. +TEST(MempolicyTest, EmptyNodemaskOnSet) { + auto cleanup = ScopedMempolicy(); + + EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, nullptr, 1), SyscallSucceeds()); + EXPECT_THAT(set_mempolicy(MPOL_BIND, nullptr, 1), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(set_mempolicy(MPOL_PREFERRED, nullptr, 1), SyscallSucceeds()); + + uint64_t nodemask = 0x1; + EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, 0), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(set_mempolicy(MPOL_BIND, &nodemask, 0), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(set_mempolicy(MPOL_PREFERRED, &nodemask, 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(MempolicyTest, QueryAvailableNodes) { + uint64_t nodemask = 0; + ASSERT_THAT( + get_mempolicy(nullptr, &nodemask, sizeof(nodemask) * BITS_PER_BYTE, + nullptr, MPOL_F_MEMS_ALLOWED), + SyscallSucceeds()); + // We can only be sure there is a single node if running on gvisor. + if (IsRunningOnGvisor()) { + EXPECT_EQ(0x1, nodemask); + } + + // MPOL_F_ADDR and MPOL_F_NODE flags may not be combined with + // MPOL_F_MEMS_ALLLOWED. + for (auto flags : + {MPOL_F_MEMS_ALLOWED | MPOL_F_ADDR, MPOL_F_MEMS_ALLOWED | MPOL_F_NODE, + MPOL_F_MEMS_ALLOWED | MPOL_F_ADDR | MPOL_F_NODE}) { + ASSERT_THAT(get_mempolicy(nullptr, &nodemask, + sizeof(nodemask) * BITS_PER_BYTE, nullptr, flags), + SyscallFailsWithErrno(EINVAL)); + } +} + +TEST(MempolicyTest, GetMempolicyQueryNodeForAddress) { + uint64_t dummy_stack_address; + auto dummy_heap_address = absl::make_unique(); + int mode; + + for (auto ptr : {&dummy_stack_address, dummy_heap_address.get()}) { + mode = -1; + ASSERT_THAT( + get_mempolicy(&mode, nullptr, 0, ptr, MPOL_F_ADDR | MPOL_F_NODE), + SyscallSucceeds()); + // If we're not running on gvisor, the address may be allocated on a + // different numa node. + if (IsRunningOnGvisor()) { + EXPECT_EQ(0, mode); + } + } + + void* invalid_address = reinterpret_cast(-1); + + // Invalid address. + ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, invalid_address, + MPOL_F_ADDR | MPOL_F_NODE), + SyscallFailsWithErrno(EFAULT)); + + // Invalid mode pointer. + ASSERT_THAT(get_mempolicy(reinterpret_cast(invalid_address), nullptr, 0, + &dummy_stack_address, MPOL_F_ADDR | MPOL_F_NODE), + SyscallFailsWithErrno(EFAULT)); +} + +TEST(MempolicyTest, GetMempolicyCanOmitPointers) { + int mode; + uint64_t nodemask; + + // Omit nodemask pointer. + ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, 0), SyscallSucceeds()); + // Omit mode pointer. + ASSERT_THAT(get_mempolicy(nullptr, &nodemask, + sizeof(nodemask) * BITS_PER_BYTE, nullptr, 0), + SyscallSucceeds()); + // Omit both pointers. + ASSERT_THAT(get_mempolicy(nullptr, nullptr, 0, nullptr, 0), + SyscallSucceeds()); +} + +TEST(MempolicyTest, GetMempolicyNextInterleaveNode) { + int mode; + // Policy for thread not yet set to MPOL_INTERLEAVE, can't query for + // the next node which will be used for allocation. + ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, MPOL_F_NODE), + SyscallFailsWithErrno(EINVAL)); + + // Set default policy for thread to MPOL_INTERLEAVE. + uint64_t nodemask = 0x1; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( + MPOL_INTERLEAVE, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); + + mode = -1; + ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, MPOL_F_NODE), + SyscallSucceeds()); + EXPECT_EQ(0, mode); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/mincore.cc b/test/syscalls/linux/mincore.cc new file mode 100644 index 000000000..c572bf5ec --- /dev/null +++ b/test/syscalls/linux/mincore.cc @@ -0,0 +1,96 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +size_t CountSetLSBs(std::vector const& vec) { + return std::count_if(begin(vec), end(vec), + [](unsigned char c) { return (c & 1) != 0; }); +} + +TEST(MincoreTest, DirtyAnonPagesAreResident) { + constexpr size_t kTestPageCount = 10; + auto const kTestMappingBytes = kTestPageCount * kPageSize; + auto m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kTestMappingBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + memset(m.ptr(), 0, m.len()); + + std::vector vec(kTestPageCount, 0); + ASSERT_THAT(mincore(m.ptr(), kTestMappingBytes, vec.data()), + SyscallSucceeds()); + EXPECT_EQ(kTestPageCount, CountSetLSBs(vec)); +} + +TEST(MincoreTest, UnalignedAddressFails) { + // Map and touch two pages, then try to mincore the second half of the first + // page + the first half of the second page. Both pages are mapped, but + // mincore should return EINVAL due to the misaligned start address. + constexpr size_t kTestPageCount = 2; + auto const kTestMappingBytes = kTestPageCount * kPageSize; + auto m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kTestMappingBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + memset(m.ptr(), 0, m.len()); + + std::vector vec(kTestPageCount, 0); + EXPECT_THAT(mincore(reinterpret_cast(m.addr() + kPageSize / 2), + kPageSize, vec.data()), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(MincoreTest, UnalignedLengthSucceedsAndIsRoundedUp) { + // Map and touch two pages, then try to mincore the first page + the first + // half of the second page. mincore should silently round up the length to + // include both pages. + constexpr size_t kTestPageCount = 2; + auto const kTestMappingBytes = kTestPageCount * kPageSize; + auto m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kTestMappingBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + memset(m.ptr(), 0, m.len()); + + std::vector vec(kTestPageCount, 0); + ASSERT_THAT(mincore(m.ptr(), kPageSize + kPageSize / 2, vec.data()), + SyscallSucceeds()); + EXPECT_EQ(kTestPageCount, CountSetLSBs(vec)); +} + +TEST(MincoreTest, ZeroLengthSucceedsAndAllowsAnyVecBelowTaskSize) { + EXPECT_THAT(mincore(nullptr, 0, nullptr), SyscallSucceeds()); +} + +TEST(MincoreTest, InvalidLengthFails) { + EXPECT_THAT(mincore(nullptr, -1, nullptr), SyscallFailsWithErrno(ENOMEM)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/mkdir.cc b/test/syscalls/linux/mkdir.cc new file mode 100644 index 000000000..84db45eb3 --- /dev/null +++ b/test/syscalls/linux/mkdir.cc @@ -0,0 +1,96 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/syscalls/linux/temp_umask.h" +#include "test/util/capability_util.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class MkdirTest : public ::testing::Test { + protected: + // SetUp creates various configurations of files. + void SetUp() override { dirname_ = NewTempAbsPath(); } + + // TearDown unlinks created files. + void TearDown() override { + // FIXME: We don't currently implement rmdir. + // We do this unconditionally because there's no harm in trying. + rmdir(dirname_.c_str()); + } + + std::string dirname_; +}; + +TEST_F(MkdirTest, DISABLED_CanCreateReadbleDir) { + ASSERT_THAT(mkdir(dirname_.c_str(), 0444), SyscallSucceeds()); + ASSERT_THAT( + open(JoinPath(dirname_, "anything").c_str(), O_RDWR | O_CREAT, 0666), + SyscallFailsWithErrno(EACCES)); +} + +TEST_F(MkdirTest, CanCreateWritableDir) { + ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); + std::string filename = JoinPath(dirname_, "anything"); + int fd; + ASSERT_THAT(fd = open(filename.c_str(), O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + ASSERT_THAT(unlink(filename.c_str()), SyscallSucceeds()); +} + +TEST_F(MkdirTest, HonorsUmask) { + constexpr mode_t kMask = 0111; + TempUmask mask(kMask); + ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); + struct stat statbuf; + ASSERT_THAT(stat(dirname_.c_str(), &statbuf), SyscallSucceeds()); + EXPECT_EQ(0777 & ~kMask, statbuf.st_mode & 0777); +} + +TEST_F(MkdirTest, HonorsUmask2) { + constexpr mode_t kMask = 0142; + TempUmask mask(kMask); + ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); + struct stat statbuf; + ASSERT_THAT(stat(dirname_.c_str(), &statbuf), SyscallSucceeds()); + EXPECT_EQ(0777 & ~kMask, statbuf.st_mode & 0777); +} + +TEST_F(MkdirTest, FailsOnDirWithoutWritePerms) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto parent = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555)); + auto dir = JoinPath(parent.path(), "foo"); + ASSERT_THAT(mkdir(dir.c_str(), 0777), SyscallFailsWithErrno(EACCES)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/mknod.cc b/test/syscalls/linux/mknod.cc new file mode 100644 index 000000000..361ca299b --- /dev/null +++ b/test/syscalls/linux/mknod.cc @@ -0,0 +1,173 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(MknodTest, RegularFile) { + std::string const node0 = NewTempAbsPathInDir("/tmp"); + std::string const node1 = NewTempAbsPathInDir("/tmp"); + ASSERT_THAT(mknod(node0.c_str(), S_IFREG, 0), SyscallSucceeds()); + ASSERT_THAT(mknod(node1.c_str(), 0, 0), SyscallSucceeds()); +} + +TEST(MknodTest, MknodAtRegularFile) { + std::string const fifo_relpath = NewTempRelPath(); + std::string const fifo = JoinPath("/tmp", fifo_relpath); + int dirfd; + ASSERT_THAT(dirfd = open("/tmp", O_RDONLY), SyscallSucceeds()); + ASSERT_THAT(mknodat(dirfd, fifo_relpath.c_str(), S_IFIFO | S_IRUSR, 0), + SyscallSucceeds()); + EXPECT_THAT(close(dirfd), SyscallSucceeds()); + + struct stat st; + ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); + EXPECT_TRUE(S_ISFIFO(st.st_mode)); +} + +TEST(MknodTest, MknodOnExistingPathFails) { + std::string const file = NewTempAbsPathInDir("/tmp"); + std::string const slink = NewTempAbsPathInDir("/tmp"); + int fd; + ASSERT_THAT(fd = open(file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + ASSERT_THAT(symlink(file.c_str(), slink.c_str()), SyscallSucceeds()); + + EXPECT_THAT(mknod(file.c_str(), S_IFREG, 0), SyscallFailsWithErrno(EEXIST)); + EXPECT_THAT(mknod(file.c_str(), S_IFIFO, 0), SyscallFailsWithErrno(EEXIST)); + EXPECT_THAT(mknod(slink.c_str(), S_IFREG, 0), SyscallFailsWithErrno(EEXIST)); + EXPECT_THAT(mknod(slink.c_str(), S_IFIFO, 0), SyscallFailsWithErrno(EEXIST)); +} + +TEST(MknodTest, UnimplementedTypesReturnError) { + if (IsRunningOnGvisor()) { + ASSERT_THAT(mknod("/tmp/a_socket", S_IFSOCK, 0), + SyscallFailsWithErrno(EOPNOTSUPP)); + } + // These will fail on linux as well since we don't have CAP_MKNOD. + ASSERT_THAT(mknod("/tmp/a_chardev", S_IFCHR, 0), + SyscallFailsWithErrno(EPERM)); + ASSERT_THAT(mknod("/tmp/a_blkdev", S_IFBLK, 0), SyscallFailsWithErrno(EPERM)); +} + +TEST(MknodTest, Fifo) { + std::string const fifo = NewTempAbsPathInDir("/tmp"); + ASSERT_THAT(mknod(fifo.c_str(), S_IFIFO | S_IRUSR | S_IWUSR, 0), + SyscallSucceeds()); + + struct stat st; + ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); + EXPECT_TRUE(S_ISFIFO(st.st_mode)); + + std::string msg = "some string"; + std::vector buf(512); + + // Read-end of the pipe. + ScopedThread t([&fifo, &buf, &msg]() { + int fd; + ASSERT_THAT(fd = open(fifo.c_str(), O_RDONLY), SyscallSucceeds()); + EXPECT_THAT(read(fd, buf.data(), buf.size()), + SyscallSucceedsWithValue(msg.length())); + EXPECT_EQ(msg, std::string(buf.data())); + EXPECT_THAT(close(fd), SyscallSucceeds()); + }); + + // Write-end of the pipe. + int wfd; + ASSERT_THAT(wfd = open(fifo.c_str(), O_WRONLY), SyscallSucceeds()); + EXPECT_THAT(write(wfd, msg.c_str(), msg.length()), + SyscallSucceedsWithValue(msg.length())); + EXPECT_THAT(close(wfd), SyscallSucceeds()); +} + +TEST(MknodTest, FifoOtrunc) { + std::string const fifo = NewTempAbsPathInDir("/tmp"); + ASSERT_THAT(mknod(fifo.c_str(), S_IFIFO | S_IRUSR | S_IWUSR, 0), + SyscallSucceeds()); + + struct stat st = {}; + ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); + EXPECT_TRUE(S_ISFIFO(st.st_mode)); + + std::string msg = "some string"; + std::vector buf(512); + // Read-end of the pipe. + ScopedThread t([&fifo, &buf, &msg]() { + int fd; + ASSERT_THAT(fd = open(fifo.c_str(), O_RDONLY), SyscallSucceeds()); + EXPECT_THAT(read(fd, buf.data(), buf.size()), + SyscallSucceedsWithValue(msg.length())); + EXPECT_EQ(msg, std::string(buf.data())); + EXPECT_THAT(close(fd), SyscallSucceeds()); + }); + + int wfd; + ASSERT_THAT(wfd = open(fifo.c_str(), O_TRUNC | O_WRONLY), SyscallSucceeds()); + EXPECT_THAT(write(wfd, msg.c_str(), msg.length()), + SyscallSucceedsWithValue(msg.length())); + EXPECT_THAT(close(wfd), SyscallSucceeds()); +} + +TEST(MknodTest, FifoTruncNoOp) { + std::string const fifo = NewTempAbsPathInDir("/tmp"); + ASSERT_THAT(mknod(fifo.c_str(), S_IFIFO | S_IRUSR | S_IWUSR, 0), + SyscallSucceeds()); + + EXPECT_THAT(truncate(fifo.c_str(), 0), SyscallFailsWithErrno(EINVAL)); + + struct stat st = {}; + ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); + EXPECT_TRUE(S_ISFIFO(st.st_mode)); + + std::string msg = "some string"; + std::vector buf(512); + // Read-end of the pipe. + ScopedThread t([&fifo, &buf, &msg]() { + int rfd = 0; + ASSERT_THAT(rfd = open(fifo.c_str(), O_RDONLY), SyscallSucceeds()); + EXPECT_THAT(ReadFd(rfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(msg.length())); + EXPECT_EQ(msg, std::string(buf.data())); + EXPECT_THAT(close(rfd), SyscallSucceeds()); + }); + + int wfd = 0; + ASSERT_THAT(wfd = open(fifo.c_str(), O_TRUNC | O_WRONLY), SyscallSucceeds()); + EXPECT_THAT(ftruncate(wfd, 0), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(WriteFd(wfd, msg.c_str(), msg.length()), + SyscallSucceedsWithValue(msg.length())); + EXPECT_THAT(ftruncate(wfd, 0), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(close(wfd), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/mmap.cc b/test/syscalls/linux/mmap.cc new file mode 100644 index 000000000..afe060d33 --- /dev/null +++ b/test/syscalls/linux/mmap.cc @@ -0,0 +1,1714 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_split.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/memory_util.h" +#include "test/util/multiprocess_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +using ::testing::Gt; + +namespace gvisor { +namespace testing { + +namespace { + +PosixErrorOr VirtualMemorySize() { + ASSIGN_OR_RETURN_ERRNO(auto contents, GetContents("/proc/self/statm")); + std::vector parts = absl::StrSplit(contents, ' '); + if (parts.empty()) { + return PosixError(EINVAL, "Unable to parse /proc/self/statm"); + } + ASSIGN_OR_RETURN_ERRNO(auto pages, Atoi(parts[0])); + return pages * getpagesize(); +} + +class MMapTest : public ::testing::Test { + protected: + // Unmap mapping, if one was made. + void TearDown() override { + if (addr_) { + EXPECT_THAT(Unmap(), SyscallSucceeds()); + } + } + + // Remembers mapping, so it can be automatically unmapped. + uintptr_t Map(uintptr_t addr, size_t length, int prot, int flags, int fd, + off_t offset) { + void* ret = + mmap(reinterpret_cast(addr), length, prot, flags, fd, offset); + + if (ret != MAP_FAILED) { + addr_ = ret; + length_ = length; + } + + return reinterpret_cast(ret); + } + + // Unmap previous mapping + int Unmap() { + if (!addr_) { + return -1; + } + + int ret = munmap(addr_, length_); + + addr_ = nullptr; + length_ = 0; + + return ret; + } + + // Msync the mapping. + int Msync() { return msync(addr_, length_, MS_SYNC); } + + // Mlock the mapping. + int Mlock() { return mlock(addr_, length_); } + + // Munlock the mapping. + int Munlock() { return munlock(addr_, length_); } + + int Protect(uintptr_t addr, size_t length, int prot) { + return mprotect(reinterpret_cast(addr), length, prot); + } + + void* addr_ = nullptr; + size_t length_ = 0; +}; + +// Matches if arg contains the same contents as std::string str. +MATCHER_P(EqualsMemory, str, "") { + if (0 == memcmp(arg, str.c_str(), str.size())) { + return true; + } + + *result_listener << "Memory did not match. Got:\n" + << absl::BytesToHexString( + std::string(static_cast(arg), str.size())) + << "Want:\n" + << absl::BytesToHexString(str); + return false; +} + +// We can't map pipes, but for different reasons. +TEST_F(MMapTest, MapPipe) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fds[0], 0), + SyscallFailsWithErrno(ENODEV)); + EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fds[1], 0), + SyscallFailsWithErrno(EACCES)); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +// It's very common to mmap /dev/zero because anonymous mappings aren't part +// of POSIX although they are widely supported. So a zero initialized memory +// region would actually come from a "file backed" /dev/zero mapping. +TEST_F(MMapTest, MapDevZeroShared) { + // This test will verify that we're able to map a page backed by /dev/zero + // as MAP_SHARED. + const FileDescriptor dev_zero = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + + // Test that we can create a RW SHARED mapping of /dev/zero. + ASSERT_THAT( + Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0), + SyscallSucceeds()); +} + +TEST_F(MMapTest, MapDevZeroPrivate) { + // This test will verify that we're able to map a page backed by /dev/zero + // as MAP_PRIVATE. + const FileDescriptor dev_zero = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + + // Test that we can create a RW SHARED mapping of /dev/zero. + ASSERT_THAT( + Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero.get(), 0), + SyscallSucceeds()); +} + +TEST_F(MMapTest, MapDevZeroNoPersistence) { + // This test will verify that two independent mappings of /dev/zero do not + // appear to reference the same "backed file." + + const FileDescriptor dev_zero1 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + const FileDescriptor dev_zero2 = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + + ASSERT_THAT( + Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero1.get(), 0), + SyscallSucceeds()); + + // Create a second mapping via the second /dev/zero fd. + void* psec_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + dev_zero2.get(), 0); + ASSERT_THAT(reinterpret_cast(psec_map), SyscallSucceeds()); + + // Always unmap. + auto cleanup_psec_map = Cleanup( + [&] { EXPECT_THAT(munmap(psec_map, kPageSize), SyscallSucceeds()); }); + + // Verify that we have independently addressed pages. + ASSERT_NE(psec_map, addr_); + + std::string buf_zero(kPageSize, 0x00); + std::string buf_ones(kPageSize, 0xFF); + + // Verify the first is actually all zeros after mmap. + EXPECT_THAT(addr_, EqualsMemory(buf_zero)); + + // Let's fill in the first mapping with 0xFF. + memcpy(addr_, buf_ones.data(), kPageSize); + + // Verify that the memcpy actually stuck in the page. + EXPECT_THAT(addr_, EqualsMemory(buf_ones)); + + // Verify that it didn't affect the second page which should be all zeros. + EXPECT_THAT(psec_map, EqualsMemory(buf_zero)); +} + +TEST_F(MMapTest, MapDevZeroSharedMultiplePages) { + // This will test that we're able to map /dev/zero over multiple pages. + const FileDescriptor dev_zero = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + + // Test that we can create a RW SHARED mapping of /dev/zero. + ASSERT_THAT(Map(0, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE, + dev_zero.get(), 0), + SyscallSucceeds()); + + std::string buf_zero(kPageSize * 2, 0x00); + std::string buf_ones(kPageSize * 2, 0xFF); + + // Verify the two pages are actually all zeros after mmap. + EXPECT_THAT(addr_, EqualsMemory(buf_zero)); + + // Fill out the pages with all ones. + memcpy(addr_, buf_ones.data(), kPageSize * 2); + + // Verify that the memcpy actually stuck in the pages. + EXPECT_THAT(addr_, EqualsMemory(buf_ones)); +} + +TEST_F(MMapTest, MapDevZeroSharedFdNoPersistence) { + // This test will verify that two independent mappings of /dev/zero do not + // appear to reference the same "backed file" even when mapped from the + // same initial fd. + const FileDescriptor dev_zero = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + + ASSERT_THAT( + Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0), + SyscallSucceeds()); + + // Create a second mapping via the same fd. + void* psec_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + dev_zero.get(), 0); + ASSERT_THAT(reinterpret_cast(psec_map), SyscallSucceeds()); + + // Always unmap. + auto cleanup_psec_map = Cleanup( + [&] { ASSERT_THAT(munmap(psec_map, kPageSize), SyscallSucceeds()); }); + + // Verify that we have independently addressed pages. + ASSERT_NE(psec_map, addr_); + + std::string buf_zero(kPageSize, 0x00); + std::string buf_ones(kPageSize, 0xFF); + + // Verify the first is actually all zeros after mmap. + EXPECT_THAT(addr_, EqualsMemory(buf_zero)); + + // Let's fill in the first mapping with 0xFF. + memcpy(addr_, buf_ones.data(), kPageSize); + + // Verify that the memcpy actually stuck in the page. + EXPECT_THAT(addr_, EqualsMemory(buf_ones)); + + // Verify that it didn't affect the second page which should be all zeros. + EXPECT_THAT(psec_map, EqualsMemory(buf_zero)); +} + +TEST_F(MMapTest, MapDevZeroSegfaultAfterUnmap) { + SetupGvisorDeathTest(); + + // This test will verify that we're able to map a page backed by /dev/zero + // as MAP_SHARED and after it's unmapped any access results in a SIGSEGV. + // This test is redundant but given the special nature of /dev/zero mappings + // it doesn't hurt. + const FileDescriptor dev_zero = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + + const auto rest = [&] { + // Test that we can create a RW SHARED mapping of /dev/zero. + TEST_PCHECK(Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + dev_zero.get(), + 0) != reinterpret_cast(MAP_FAILED)); + + // Confirm that accesses after the unmap result in a SIGSEGV. + // + // N.B. We depend on this process being single-threaded to ensure there + // can't be another mmap to map addr before the dereference below. + void* addr_saved = addr_; // Unmap resets addr_. + TEST_PCHECK(Unmap() == 0); + *reinterpret_cast(addr_saved) = 0xFF; + }; + + EXPECT_THAT(InForkedProcess(rest), + IsPosixErrorOkAndHolds(W_EXITCODE(0, SIGSEGV))); +} + +TEST_F(MMapTest, MapDevZeroUnaligned) { + const FileDescriptor dev_zero = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); + const size_t size = kPageSize + kPageSize / 2; + const std::string buf_zero(size, 0x00); + + ASSERT_THAT( + Map(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0), + SyscallSucceeds()); + EXPECT_THAT(addr_, EqualsMemory(buf_zero)); + ASSERT_THAT(Unmap(), SyscallSucceeds()); + + ASSERT_THAT( + Map(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero.get(), 0), + SyscallSucceeds()); + EXPECT_THAT(addr_, EqualsMemory(buf_zero)); +} + +// We can't map _some_ character devices. +TEST_F(MMapTest, MapCharDevice) { + const FileDescriptor cdevfd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/random", 0, 0)); + EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, cdevfd.get(), 0), + SyscallFailsWithErrno(ENODEV)); +} + +// We can't map directories. +TEST_F(MMapTest, MapDirectory) { + const FileDescriptor dirfd = + ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), 0, 0)); + EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, dirfd.get(), 0), + SyscallFailsWithErrno(ENODEV)); +} + +// We can map *something* +TEST_F(MMapTest, MapAnything) { + EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceedsWithValue(Gt(0))); +} + +// Map length < PageSize allowed +TEST_F(MMapTest, SmallMap) { + EXPECT_THAT(Map(0, 128, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); +} + +// Hint address doesn't break anything. +// Note: there is no requirement we actually get the hint address +TEST_F(MMapTest, HintAddress) { + EXPECT_THAT( + Map(0x30000000, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); +} + +// MAP_FIXED gives us exactly the requested address +TEST_F(MMapTest, MapFixed) { + EXPECT_THAT(Map(0x30000000, kPageSize, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0), + SyscallSucceedsWithValue(0x30000000)); +} + +// 64-bit addresses work too +#ifdef __x86_64__ +TEST_F(MMapTest, MapFixed64) { + EXPECT_THAT(Map(0x300000000000, kPageSize, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0), + SyscallSucceedsWithValue(0x300000000000)); +} +#endif + +// MAP_STACK allowed. +// There isn't a good way to verify it did anything. +TEST_F(MMapTest, MapStack) { + EXPECT_THAT(Map(0, kPageSize, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0), + SyscallSucceeds()); +} + +// MAP_LOCKED allowed. +// There isn't a good way to verify it did anything. +TEST_F(MMapTest, MapLocked) { + EXPECT_THAT(Map(0, kPageSize, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0), + SyscallSucceeds()); +} + +// MAP_PRIVATE or MAP_SHARED must be passed +TEST_F(MMapTest, NotPrivateOrShared) { + EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_ANONYMOUS, -1, 0), + SyscallFailsWithErrno(EINVAL)); +} + +// Only one of MAP_PRIVATE or MAP_SHARED may be passed +TEST_F(MMapTest, PrivateAndShared) { + EXPECT_THAT(Map(0, kPageSize, PROT_NONE, + MAP_PRIVATE | MAP_SHARED | MAP_ANONYMOUS, -1, 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(MMapTest, FixedAlignment) { + // Addr must be page aligned (MAP_FIXED) + EXPECT_THAT(Map(0x30000001, kPageSize, PROT_NONE, + MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0), + SyscallFailsWithErrno(EINVAL)); +} + +// Non-MAP_FIXED address does not need to be page aligned +TEST_F(MMapTest, NonFixedAlignment) { + EXPECT_THAT( + Map(0x30000001, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); +} + +// Length = 0 results in EINVAL. +TEST_F(MMapTest, InvalidLength) { + EXPECT_THAT(Map(0, 0, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallFailsWithErrno(EINVAL)); +} + +// Bad fd not allowed. +TEST_F(MMapTest, BadFd) { + EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_PRIVATE, 999, 0), + SyscallFailsWithErrno(EBADF)); +} + +// Mappings are writable. +TEST_F(MMapTest, ProtWrite) { + uint64_t addr; + constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; + + EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + // This shouldn't cause a SIGSEGV. + memset(reinterpret_cast(addr), 42, kPageSize); + + // The written data should actually be there. + EXPECT_EQ( + 0, memcmp(reinterpret_cast(addr), kFirstWord, sizeof(kFirstWord))); +} + +// "Write-only" mappings are writable *and* readable. +TEST_F(MMapTest, ProtWriteOnly) { + uint64_t addr; + constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; + + EXPECT_THAT( + addr = Map(0, kPageSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + // This shouldn't cause a SIGSEGV. + memset(reinterpret_cast(addr), 42, kPageSize); + + // The written data should actually be there. + EXPECT_EQ( + 0, memcmp(reinterpret_cast(addr), kFirstWord, sizeof(kFirstWord))); +} + +// "Write-only" mappings are readable. +// +// This is distinct from above to ensure the page is accessible even if the +// initial fault is a write fault. +TEST_F(MMapTest, ProtWriteOnlyReadable) { + uint64_t addr; + constexpr uint64_t kFirstWord = 0; + + EXPECT_THAT( + addr = Map(0, kPageSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), &kFirstWord, + sizeof(kFirstWord))); +} + +// Mappings are writable after mprotect from PROT_NONE to PROT_READ|PROT_WRITE. +TEST_F(MMapTest, ProtectProtWrite) { + uint64_t addr; + constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; + + EXPECT_THAT( + addr = Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_WRITE), + SyscallSucceeds()); + + // This shouldn't cause a SIGSEGV. + memset(reinterpret_cast(addr), 42, kPageSize); + + // The written data should actually be there. + EXPECT_EQ( + 0, memcmp(reinterpret_cast(addr), kFirstWord, sizeof(kFirstWord))); +} + +// SIGSEGV raised when reading PROT_NONE memory +TEST_F(MMapTest, ProtNoneDeath) { + SetupGvisorDeathTest(); + + uintptr_t addr; + + ASSERT_THAT( + addr = Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + EXPECT_EXIT(*reinterpret_cast(addr), + ::testing::KilledBySignal(SIGSEGV), ""); +} + +// SIGSEGV raised when writing PROT_READ only memory +TEST_F(MMapTest, ReadOnlyDeath) { + SetupGvisorDeathTest(); + + uintptr_t addr; + + ASSERT_THAT( + addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + EXPECT_EXIT(*reinterpret_cast(addr) = 42, + ::testing::KilledBySignal(SIGSEGV), ""); +} + +// Writable mapping mprotect'd to read-only should not be writable. +TEST_F(MMapTest, MprotectReadOnlyDeath) { + SetupGvisorDeathTest(); + + uintptr_t addr; + + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + volatile int* val = reinterpret_cast(addr); + + // Copy to ensure page is mapped in. + *val = 42; + + ASSERT_THAT(Protect(addr, kPageSize, PROT_READ), SyscallSucceeds()); + + // Now it shouldn't be writable. + EXPECT_EXIT(*val = 0, ::testing::KilledBySignal(SIGSEGV), ""); +} + +// Verify that calling mprotect an address that's not page aligned fails. +TEST_F(MMapTest, MprotectNotPageAligned) { + uintptr_t addr; + + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + ASSERT_THAT(Protect(addr + 1, kPageSize - 1, PROT_READ), + SyscallFailsWithErrno(EINVAL)); +} + +// Verify that calling mprotect with an absurdly huge length fails. +TEST_F(MMapTest, MprotectHugeLength) { + uintptr_t addr; + + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + ASSERT_THAT(Protect(addr, static_cast(-1), PROT_READ), + SyscallFailsWithErrno(ENOMEM)); +} + +#if defined(__x86_64__) || defined(__i386__) +// This code is equivalent in 32 and 64-bit mode +const uint8_t machine_code[] = { + 0xb8, 0x2a, 0x00, 0x00, 0x00, // movl $42, %eax + 0xc3, // retq +}; + +// PROT_EXEC allows code execution +TEST_F(MMapTest, ProtExec) { + uintptr_t addr; + uint32_t (*func)(void); + + EXPECT_THAT(addr = Map(0, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + memcpy(reinterpret_cast(addr), machine_code, sizeof(machine_code)); + + func = reinterpret_cast(addr); + + EXPECT_EQ(42, func()); +} + +// No PROT_EXEC disallows code execution +TEST_F(MMapTest, NoProtExecDeath) { + SetupGvisorDeathTest(); + + uintptr_t addr; + uint32_t (*func)(void); + + EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + + memcpy(reinterpret_cast(addr), machine_code, sizeof(machine_code)); + + func = reinterpret_cast(addr); + + EXPECT_EXIT(func(), ::testing::KilledBySignal(SIGSEGV), ""); +} +#endif + +TEST_F(MMapTest, NoExceedLimitData) { + void* prevbrk; + void* target_brk; + struct rlimit setlim; + + prevbrk = sbrk(0); + ASSERT_NE(-1, reinterpret_cast(prevbrk)); + target_brk = reinterpret_cast(prevbrk) + 1; + + setlim.rlim_cur = RLIM_INFINITY; + setlim.rlim_max = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); + EXPECT_THAT(brk(target_brk), SyscallSucceedsWithValue(0)); +} + +TEST_F(MMapTest, ExceedLimitData) { + // To unit test this more precisely, we'd need access to the mm's start_brk + // and end_brk, which we don't have direct access to :/ + void* prevbrk; + void* target_brk; + struct rlimit setlim; + + prevbrk = sbrk(0); + ASSERT_NE(-1, reinterpret_cast(prevbrk)); + target_brk = reinterpret_cast(prevbrk) + 8192; + + setlim.rlim_cur = 0; + setlim.rlim_max = RLIM_INFINITY; + // Set RLIMIT_DATA very low so any subsequent brk() calls fail. + // Reset RLIMIT_DATA during teardown step. + ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); + EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM)); + // Teardown step... + setlim.rlim_cur = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); +} + +TEST_F(MMapTest, ExceedLimitDataPrlimit) { + // To unit test this more precisely, we'd need access to the mm's start_brk + // and end_brk, which we don't have direct access to :/ + void* prevbrk; + void* target_brk; + struct rlimit setlim; + + prevbrk = sbrk(0); + ASSERT_NE(-1, reinterpret_cast(prevbrk)); + target_brk = reinterpret_cast(prevbrk) + 8192; + + setlim.rlim_cur = 0; + setlim.rlim_max = RLIM_INFINITY; + // Set RLIMIT_DATA very low so any subsequent brk() calls fail. + // Reset RLIMIT_DATA during teardown step. + ASSERT_THAT(prlimit(0, RLIMIT_DATA, &setlim, nullptr), SyscallSucceeds()); + EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM)); + // Teardown step... + setlim.rlim_cur = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); +} + +TEST_F(MMapTest, ExceedLimitDataPrlimitPID) { + // To unit test this more precisely, we'd need access to the mm's start_brk + // and end_brk, which we don't have direct access to :/ + void* prevbrk; + void* target_brk; + struct rlimit setlim; + + prevbrk = sbrk(0); + ASSERT_NE(-1, reinterpret_cast(prevbrk)); + target_brk = reinterpret_cast(prevbrk) + 8192; + + setlim.rlim_cur = 0; + setlim.rlim_max = RLIM_INFINITY; + // Set RLIMIT_DATA very low so any subsequent brk() calls fail. + // Reset RLIMIT_DATA during teardown step. + ASSERT_THAT(prlimit(syscall(__NR_gettid), RLIMIT_DATA, &setlim, nullptr), + SyscallSucceeds()); + EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM)); + // Teardown step... + setlim.rlim_cur = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); +} + +TEST_F(MMapTest, NoExceedLimitAS) { + constexpr uint64_t kAllocBytes = 200 << 20; + // Add some headroom to the AS limit in case of e.g. unexpected stack + // expansion. + constexpr uint64_t kExtraASBytes = kAllocBytes + (20 << 20); + static_assert(kAllocBytes < kExtraASBytes, + "test depends on allocation not exceeding AS limit"); + + auto vss = ASSERT_NO_ERRNO_AND_VALUE(VirtualMemorySize()); + struct rlimit setlim; + setlim.rlim_cur = vss + kExtraASBytes; + setlim.rlim_max = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_AS, &setlim), SyscallSucceeds()); + EXPECT_THAT( + Map(0, kAllocBytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceedsWithValue(Gt(0))); +} + +TEST_F(MMapTest, ExceedLimitAS) { + constexpr uint64_t kAllocBytes = 200 << 20; + // Add some headroom to the AS limit in case of e.g. unexpected stack + // expansion. + constexpr uint64_t kExtraASBytes = 20 << 20; + static_assert(kAllocBytes > kExtraASBytes, + "test depends on allocation exceeding AS limit"); + + auto vss = ASSERT_NO_ERRNO_AND_VALUE(VirtualMemorySize()); + struct rlimit setlim; + setlim.rlim_cur = vss + kExtraASBytes; + setlim.rlim_max = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_AS, &setlim), SyscallSucceeds()); + EXPECT_THAT( + Map(0, kAllocBytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallFailsWithErrno(ENOMEM)); +} + +// Tests that setting an anonymous mmap to PROT_NONE doesn't free the memory. +TEST_F(MMapTest, SettingProtNoneDoesntFreeMemory) { + uintptr_t addr; + constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; + + EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceedsWithValue(Gt(0))); + + memset(reinterpret_cast(addr), 42, kPageSize); + + ASSERT_THAT(Protect(addr, kPageSize, PROT_NONE), SyscallSucceeds()); + ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_WRITE), + SyscallSucceeds()); + + // The written data should still be there. + EXPECT_EQ( + 0, memcmp(reinterpret_cast(addr), kFirstWord, sizeof(kFirstWord))); +} + +constexpr char kFileContents[] = "Hello World!"; + +class MMapFileTest : public MMapTest { + protected: + FileDescriptor fd_; + std::string filename_; + + // Open a file for read/write + void SetUp() override { + MMapTest::SetUp(); + + filename_ = NewTempAbsPath(); + fd_ = ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_CREAT | O_RDWR, 0644)); + + // Extend file so it can be written once mapped. Deliberately make the file + // only half a page in size, so we can test what happens when we access the + // second half. + // Use ftruncate(2) once the sentry supports it. + char zero = 0; + size_t count = 0; + do { + const DisableSave ds; // saving 2048 times is slow and useless. + Write(&zero, 1), SyscallSucceedsWithValue(1); + } while (++count < (kPageSize / 2)); + ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + } + + // Close and delete file + void TearDown() override { + MMapTest::TearDown(); + fd_.reset(); // Make sure the files is closed before we unlink it. + ASSERT_THAT(unlink(filename_.c_str()), SyscallSucceeds()); + } + + ssize_t Read(char* buf, size_t count) { + ssize_t len = 0; + do { + ssize_t ret = read(fd_.get(), buf, count); + if (ret < 0) { + return ret; + } else if (ret == 0) { + return len; + } + + len += ret; + buf += ret; + } while (len < static_cast(count)); + + return len; + } + + ssize_t Write(const char* buf, size_t count) { + ssize_t len = 0; + do { + ssize_t ret = write(fd_.get(), buf, count); + if (ret < 0) { + return ret; + } else if (ret == 0) { + return len; + } + + len += ret; + buf += ret; + } while (len < static_cast(count)); + + return len; + } +}; + +// MAP_POPULATE allowed. +// There isn't a good way to verify it actually did anything. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, MapPopulate) { + ASSERT_THAT( + Map(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd_.get(), 0), + SyscallSucceeds()); +} + +// MAP_POPULATE on a short file. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, MapPopulateShort) { + ASSERT_THAT(Map(0, 2 * kPageSize, PROT_READ, MAP_PRIVATE | MAP_POPULATE, + fd_.get(), 0), + SyscallSucceeds()); +} + +// Read contents from mapped file. +TEST_F(MMapFileTest, Read) { + size_t len = strlen(kFileContents); + ASSERT_EQ(len, Write(kFileContents, len)); + + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd_.get(), 0), + SyscallSucceeds()); + + EXPECT_THAT(reinterpret_cast(addr), + EqualsMemory(std::string(kFileContents))); +} + +// Map at an offset. +TEST_F(MMapFileTest, MapOffset) { + ASSERT_THAT(lseek(fd_.get(), kPageSize, SEEK_SET), SyscallSucceeds()); + + size_t len = strlen(kFileContents); + ASSERT_EQ(len, Write(kFileContents, len)); + + uintptr_t addr; + ASSERT_THAT( + addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd_.get(), kPageSize), + SyscallSucceeds()); + + EXPECT_THAT(reinterpret_cast(addr), + EqualsMemory(std::string(kFileContents))); +} + +TEST_F(MMapFileTest, MapOffsetBeyondEnd) { + SetupGvisorDeathTest(); + + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd_.get(), 10 * kPageSize), + SyscallSucceeds()); + + // Touching the memory causes SIGBUS. + size_t len = strlen(kFileContents); + EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr)), + ::testing::KilledBySignal(SIGBUS), ""); +} + +// Verify mmap fails when sum of length and offset overflows. +TEST_F(MMapFileTest, MapLengthPlusOffsetOverflows) { + const size_t length = static_cast(-kPageSize); + const off_t offset = kPageSize; + ASSERT_THAT(Map(0, length, PROT_READ, MAP_PRIVATE, fd_.get(), offset), + SyscallFailsWithErrno(ENOMEM)); +} + +// MAP_PRIVATE PROT_WRITE is allowed on read-only FDs. +TEST_F(MMapFileTest, WritePrivateOnReadOnlyFd) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY)); + + uintptr_t addr; + EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd.get(), 0), + SyscallSucceeds()); + + // Touch the page to ensure the kernel didn't lie about writability. + size_t len = strlen(kFileContents); + std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr)); +} + +// MAP_PRIVATE PROT_READ is not allowed on write-only FDs. +TEST_F(MMapFileTest, ReadPrivateOnWriteOnlyFd) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY)); + + uintptr_t addr; + EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd.get(), 0), + SyscallFailsWithErrno(EACCES)); +} + +// MAP_SHARED PROT_WRITE not allowed on read-only FDs. +TEST_F(MMapFileTest, WriteSharedOnReadOnlyFd) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY)); + + uintptr_t addr; + EXPECT_THAT( + addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0), + SyscallFailsWithErrno(EACCES)); +} + +// MAP_SHARED PROT_READ not allowed on write-only FDs. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, ReadSharedOnWriteOnlyFd) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY)); + + uintptr_t addr; + EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd.get(), 0), + SyscallFailsWithErrno(EACCES)); +} + +// MAP_SHARED PROT_WRITE not allowed on write-only FDs. +// The FD must always be readable. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, WriteSharedOnWriteOnlyFd) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY)); + + uintptr_t addr; + EXPECT_THAT(addr = Map(0, kPageSize, PROT_WRITE, MAP_SHARED, fd.get(), 0), + SyscallFailsWithErrno(EACCES)); +} + +// Overwriting the contents of a file mapped MAP_SHARED PROT_READ +// should cause the new data to be reflected in the mapping. +TEST_F(MMapFileTest, ReadSharedConsistentWithOverwrite) { + // Start from scratch. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Expand the file to two pages and dirty them. + std::string bufA(kPageSize, 'a'); + ASSERT_THAT(Write(bufA.c_str(), bufA.size()), + SyscallSucceedsWithValue(bufA.size())); + std::string bufB(kPageSize, 'b'); + ASSERT_THAT(Write(bufB.c_str(), bufB.size()), + SyscallSucceedsWithValue(bufB.size())); + + // Map the page. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // Check that the mapping contains the right file data. + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), bufA.c_str(), kPageSize)); + EXPECT_EQ(0, memcmp(reinterpret_cast(addr + kPageSize), bufB.c_str(), + kPageSize)); + + // Start at the beginning of the file. + ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + + // Swap the write pattern. + ASSERT_THAT(Write(bufB.c_str(), bufB.size()), + SyscallSucceedsWithValue(bufB.size())); + ASSERT_THAT(Write(bufA.c_str(), bufA.size()), + SyscallSucceedsWithValue(bufA.size())); + + // Check that the mapping got updated. + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), bufB.c_str(), kPageSize)); + EXPECT_EQ(0, memcmp(reinterpret_cast(addr + kPageSize), bufA.c_str(), + kPageSize)); +} + +// Partially overwriting a file mapped MAP_SHARED PROT_READ should be reflected +// in the mapping. +TEST_F(MMapFileTest, ReadSharedConsistentWithPartialOverwrite) { + // Start from scratch. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Expand the file to two pages and dirty them. + std::string bufA(kPageSize, 'a'); + ASSERT_THAT(Write(bufA.c_str(), bufA.size()), + SyscallSucceedsWithValue(bufA.size())); + std::string bufB(kPageSize, 'b'); + ASSERT_THAT(Write(bufB.c_str(), bufB.size()), + SyscallSucceedsWithValue(bufB.size())); + + // Map the page. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // Check that the mapping contains the right file data. + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), bufA.c_str(), kPageSize)); + EXPECT_EQ(0, memcmp(reinterpret_cast(addr + kPageSize), bufB.c_str(), + kPageSize)); + + // Start at the beginning of the file. + ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + + // Do a partial overwrite, spanning both pages. + std::string bufC(kPageSize + (kPageSize / 2), 'c'); + ASSERT_THAT(Write(bufC.c_str(), bufC.size()), + SyscallSucceedsWithValue(bufC.size())); + + // Check that the mapping got updated. + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), bufC.c_str(), + kPageSize + (kPageSize / 2))); + EXPECT_EQ(0, + memcmp(reinterpret_cast(addr + kPageSize + (kPageSize / 2)), + bufB.c_str(), kPageSize / 2)); +} + +// Overwriting a file mapped MAP_SHARED PROT_READ should be reflected in the +// mapping and the file. +TEST_F(MMapFileTest, ReadSharedConsistentWithWriteAndFile) { + // Start from scratch. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Expand the file to two full pages and dirty it. + std::string bufA(2 * kPageSize, 'a'); + ASSERT_THAT(Write(bufA.c_str(), bufA.size()), + SyscallSucceedsWithValue(bufA.size())); + + // Map only the first page. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // Prepare to overwrite the file contents. + ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + + // Overwrite everything, beyond the mapped portion. + std::string bufB(2 * kPageSize, 'b'); + ASSERT_THAT(Write(bufB.c_str(), bufB.size()), + SyscallSucceedsWithValue(bufB.size())); + + // What the mapped portion should now look like. + std::string bufMapped(kPageSize, 'b'); + + // Expect that the mapped portion is consistent. + EXPECT_EQ( + 0, memcmp(reinterpret_cast(addr), bufMapped.c_str(), kPageSize)); + + // Prepare to read the entire file contents. + ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + + // Expect that the file was fully updated. + std::vector bufFile(2 * kPageSize); + ASSERT_THAT(Read(bufFile.data(), bufFile.size()), + SyscallSucceedsWithValue(bufFile.size())); + // Cast to void* to avoid EXPECT_THAT assuming bufFile.data() is a + // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C + // std::string, possibly overruning the buffer. + EXPECT_THAT(reinterpret_cast(bufFile.data()), EqualsMemory(bufB)); +} + +// Write data to mapped file. +TEST_F(MMapFileTest, WriteShared) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + size_t len = strlen(kFileContents); + memcpy(reinterpret_cast(addr), kFileContents, len); + + // The file may not actually be updated until munmap is called. + ASSERT_THAT(Unmap(), SyscallSucceeds()); + + std::vector buf(len); + ASSERT_THAT(Read(buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a + // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C + // std::string, possibly overruning the buffer. + EXPECT_THAT(reinterpret_cast(buf.data()), + EqualsMemory(std::string(kFileContents))); +} + +// Write data to portion of mapped page beyond the end of the file. +// These writes are not reflected in the file. +TEST_F(MMapFileTest, WriteSharedBeyondEnd) { + // The file is only half of a page. We map an entire page. Writes to the + // end of the mapping must not be reflected in the file. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + // First half; this is reflected in the file. + std::string first(kPageSize / 2, 'A'); + memcpy(reinterpret_cast(addr), first.c_str(), first.size()); + + // Second half; this is not reflected in the file. + std::string second(kPageSize / 2, 'B'); + memcpy(reinterpret_cast(addr + kPageSize / 2), second.c_str(), + second.size()); + + // The file may not actually be updated until munmap is called. + ASSERT_THAT(Unmap(), SyscallSucceeds()); + + // Big enough to fit the entire page, if the writes are mistakenly written to + // the file. + std::vector buf(kPageSize); + + // Only the first half is in the file. + ASSERT_THAT(Read(buf.data(), buf.size()), + SyscallSucceedsWithValue(first.size())); + // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a + // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C + // std::string, possibly overruning the buffer. + EXPECT_THAT(reinterpret_cast(buf.data()), EqualsMemory(first)); +} + +// The portion of a mapped page that becomes part of the file after a truncate +// is reflected in the file. +TEST_F(MMapFileTest, WriteSharedTruncateUp) { + // The file is only half of a page. We map an entire page. Writes to the + // end of the mapping must not be reflected in the file. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + // First half; this is reflected in the file. + std::string first(kPageSize / 2, 'A'); + memcpy(reinterpret_cast(addr), first.c_str(), first.size()); + + // Second half; this is not reflected in the file now (see + // WriteSharedBeyondEnd), but will be after the truncate. + std::string second(kPageSize / 2, 'B'); + memcpy(reinterpret_cast(addr + kPageSize / 2), second.c_str(), + second.size()); + + // Extend the file to a full page. The second half of the page will be + // reflected in the file. + EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); + + // The file may not actually be updated until munmap is called. + ASSERT_THAT(Unmap(), SyscallSucceeds()); + + // The whole page is in the file. + std::vector buf(kPageSize); + ASSERT_THAT(Read(buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a + // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C + // std::string, possibly overruning the buffer. + EXPECT_THAT(reinterpret_cast(buf.data()), EqualsMemory(first)); + EXPECT_THAT(reinterpret_cast(buf.data() + kPageSize / 2), + EqualsMemory(second)); +} + +TEST_F(MMapFileTest, ReadSharedTruncateDownThenUp) { + // Start from scratch. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Expand the file to a full page and dirty it. + std::string buf(kPageSize, 'a'); + ASSERT_THAT(Write(buf.c_str(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + // Map the page. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // Check that the memory contains he file data. + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), buf.c_str(), kPageSize)); + + // Truncate down, then up. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); + + // Check that the memory was zeroed. + std::string zeroed(kPageSize, '\0'); + EXPECT_EQ(0, + memcmp(reinterpret_cast(addr), zeroed.c_str(), kPageSize)); + + // The file may not actually be updated until msync is called. + ASSERT_THAT(Msync(), SyscallSucceeds()); + + // Prepare to read the entire file contents. + ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); + + // Expect that the file is fully updated. + std::vector bufFile(kPageSize); + ASSERT_THAT(Read(bufFile.data(), bufFile.size()), + SyscallSucceedsWithValue(bufFile.size())); + EXPECT_EQ(0, memcmp(bufFile.data(), zeroed.c_str(), kPageSize)); +} + +TEST_F(MMapFileTest, WriteSharedTruncateDownThenUp) { + // The file is only half of a page. We map an entire page. Writes to the + // end of the mapping must not be reflected in the file. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + // First half; this will be deleted by truncate(0). + std::string first(kPageSize / 2, 'A'); + memcpy(reinterpret_cast(addr), first.c_str(), first.size()); + + // Truncate down, then up. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); + + // The whole page is zeroed in memory. + std::string zeroed(kPageSize, '\0'); + EXPECT_EQ(0, + memcmp(reinterpret_cast(addr), zeroed.c_str(), kPageSize)); + + // The file may not actually be updated until munmap is called. + ASSERT_THAT(Unmap(), SyscallSucceeds()); + + // The whole file is also zeroed. + std::vector buf(kPageSize); + ASSERT_THAT(Read(buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a + // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C + // std::string, possibly overruning the buffer. + EXPECT_THAT(reinterpret_cast(buf.data()), EqualsMemory(zeroed)); +} + +TEST_F(MMapFileTest, ReadSharedTruncateSIGBUS) { + SetupGvisorDeathTest(); + + // Start from scratch. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Expand the file to a full page and dirty it. + std::string buf(kPageSize, 'a'); + ASSERT_THAT(Write(buf.c_str(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + // Map the page. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // Check that the mapping contains the file data. + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), buf.c_str(), kPageSize)); + + // Truncate down. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Accessing the truncated region should cause a SIGBUS. + std::vector in(kPageSize); + EXPECT_EXIT( + std::copy(reinterpret_cast(addr), + reinterpret_cast(addr) + kPageSize, in.data()), + ::testing::KilledBySignal(SIGBUS), ""); +} + +TEST_F(MMapFileTest, WriteSharedTruncateSIGBUS) { + SetupGvisorDeathTest(); + + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + // Touch the memory to be sure it really is mapped. + size_t len = strlen(kFileContents); + memcpy(reinterpret_cast(addr), kFileContents, len); + + // Truncate down. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Accessing the truncated file should cause a SIGBUS. + EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr)), + ::testing::KilledBySignal(SIGBUS), ""); +} + +TEST_F(MMapFileTest, ReadSharedTruncatePartialPage) { + // Start from scratch. + EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); + + // Dirty the file. + std::string buf(kPageSize, 'a'); + ASSERT_THAT(Write(buf.c_str(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + // Map a page. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // Truncate to half of the page. + EXPECT_THAT(ftruncate(fd_.get(), kPageSize / 2), SyscallSucceeds()); + + // First half of the page untouched. + EXPECT_EQ(0, + memcmp(reinterpret_cast(addr), buf.data(), kPageSize / 2)); + + // Second half is zeroed. + std::string zeroed(kPageSize / 2, '\0'); + EXPECT_EQ(0, memcmp(reinterpret_cast(addr + kPageSize / 2), + zeroed.c_str(), kPageSize / 2)); +} + +// Page can still be accessed and contents are intact after truncating a partial +// page. +TEST_F(MMapFileTest, WriteSharedTruncatePartialPage) { + // Expand the file to a full page. + EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); + + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + // Fill the entire page. + std::string contents(kPageSize, 'A'); + memcpy(reinterpret_cast(addr), contents.c_str(), contents.size()); + + // Truncate half of the page. + EXPECT_THAT(ftruncate(fd_.get(), kPageSize / 2), SyscallSucceeds()); + + // First half of the page untouched. + EXPECT_EQ(0, memcmp(reinterpret_cast(addr), contents.c_str(), + kPageSize / 2)); + + // Second half zeroed. + std::string zeroed(kPageSize / 2, '\0'); + EXPECT_EQ(0, memcmp(reinterpret_cast(addr + kPageSize / 2), + zeroed.c_str(), kPageSize / 2)); +} + +// MAP_PRIVATE writes are not carried through to the underlying file. +TEST_F(MMapFileTest, WritePrivate) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd_.get(), 0), + SyscallSucceeds()); + + size_t len = strlen(kFileContents); + memcpy(reinterpret_cast(addr), kFileContents, len); + + // The file should not be updated, but if it mistakenly is, it may not be + // until after munmap is called. + ASSERT_THAT(Unmap(), SyscallSucceeds()); + + std::vector buf(len); + ASSERT_THAT(Read(buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a + // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C + // std::string, possibly overruning the buffer. + EXPECT_THAT(reinterpret_cast(buf.data()), + EqualsMemory(std::string(len, '\0'))); +} + +// SIGBUS raised when writing past end of file to a private mapping. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, SigBusDeathWritePrivate) { + SetupGvisorDeathTest(); + + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd_.get(), 0), + SyscallSucceeds()); + + // MMapFileTest makes a file kPageSize/2 long. The entire first page will be + // accessible. Write just beyond that. + size_t len = strlen(kFileContents); + EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr + kPageSize)), + ::testing::KilledBySignal(SIGBUS), ""); +} + +// SIGBUS raised when reading past end of file on a shared mapping. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, SigBusDeathReadShared) { + SetupGvisorDeathTest(); + + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // MMapFileTest makes a file kPageSize/2 long. The entire first page will be + // accessible. Read just beyond that. + std::vector in(kPageSize); + EXPECT_EXIT( + std::copy(reinterpret_cast(addr + kPageSize), + reinterpret_cast(addr + kPageSize) + kPageSize, + in.data()), + ::testing::KilledBySignal(SIGBUS), ""); +} + +// SIGBUS raised when reading past end of file on a shared mapping. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, SigBusDeathWriteShared) { + SetupGvisorDeathTest(); + + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + // MMapFileTest makes a file kPageSize/2 long. The entire first page will be + // accessible. Write just beyond that. + size_t len = strlen(kFileContents); + EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr + kPageSize)), + ::testing::KilledBySignal(SIGBUS), ""); +} + +// Tests that SIGBUS is not raised when writing to a file-mapped page before +// EOF, even if part of the mapping extends beyond EOF. +TEST_F(MMapFileTest, NoSigBusOnPagesBeforeEOF) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd_.get(), 0), + SyscallSucceeds()); + + // The test passes if this survives. + size_t len = strlen(kFileContents); + std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr)); +} + +// Tests that SIGBUS is not raised when writing to a file-mapped page containing +// EOF, *after* the EOF for a private mapping. +TEST_F(MMapFileTest, NoSigBusOnPageContainingEOFWritePrivate) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd_.get(), 0), + SyscallSucceeds()); + + // The test passes if this survives. (Technically addr+kPageSize/2 is already + // beyond EOF, but +1 to check for fencepost errors.) + size_t len = strlen(kFileContents); + std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr + (kPageSize / 2) + 1)); +} + +// Tests that SIGBUS is not raised when reading from a file-mapped page +// containing EOF, *after* the EOF for a shared mapping. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, NoSigBusOnPageContainingEOFReadShared) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + // The test passes if this survives. (Technically addr+kPageSize/2 is already + // beyond EOF, but +1 to check for fencepost errors.) + auto* start = reinterpret_cast(addr + (kPageSize / 2) + 1); + size_t len = strlen(kFileContents); + std::vector in(len); + std::copy(start, start + len, in.data()); +} + +// Tests that SIGBUS is not raised when writing to a file-mapped page containing +// EOF, *after* the EOF for a shared mapping. +// +// FIXME: Parameterize. +TEST_F(MMapFileTest, NoSigBusOnPageContainingEOFWriteShared) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + // The test passes if this survives. (Technically addr+kPageSize/2 is already + // beyond EOF, but +1 to check for fencepost errors.) + size_t len = strlen(kFileContents); + std::copy(kFileContents, kFileContents + len, + reinterpret_cast(addr + (kPageSize / 2) + 1)); +} + +// Tests that reading from writable shared file-mapped pages succeeds. +// +// On most platforms this is trivial, but when the file is mapped via the sentry +// page cache (which does not yet support writing to shared mappings), a bug +// caused reads to fail unnecessarily on such mappings. +TEST_F(MMapFileTest, ReadingWritableSharedFilePageSucceeds) { + uintptr_t addr; + size_t len = strlen(kFileContents); + + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + std::vector buf(kPageSize); + // The test passes if this survives. + std::copy(reinterpret_cast(addr), + reinterpret_cast(addr) + len, buf.data()); +} + +// Tests that EFAULT is returned when invoking a syscall that requires the OS to +// read past end of file (resulting in a fault in sentry context in the gVisor +// case). +TEST_F(MMapFileTest, InternalSigBus) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd_.get(), 0), + SyscallSucceeds()); + + // This depends on the fact that gVisor implements pipes internally. + int pipefd[2]; + ASSERT_THAT(pipe(pipefd), SyscallSucceeds()); + EXPECT_THAT( + write(pipefd[1], reinterpret_cast(addr + kPageSize), kPageSize), + SyscallFailsWithErrno(EFAULT)); + + EXPECT_THAT(close(pipefd[0]), SyscallSucceeds()); + EXPECT_THAT(close(pipefd[1]), SyscallSucceeds()); +} + +// Like InternalSigBus, but test the WriteZerosAt path by reading from +// /dev/zero to a shared mapping (so that the SIGBUS isn't caught during +// copy-on-write breaking). +TEST_F(MMapFileTest, InternalSigBusZeroing) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + + const FileDescriptor dev_zero = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); + EXPECT_THAT(read(dev_zero.get(), reinterpret_cast(addr + kPageSize), + kPageSize), + SyscallFailsWithErrno(EFAULT)); +} + +// Checks that mmaps with a length of uint64_t(-PAGE_SIZE + 1) or greater do not +// induce a sentry panic (due to "rounding up" to 0). +TEST_F(MMapTest, HugeLength) { + EXPECT_THAT(Map(0, static_cast(-kPageSize + 1), PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallFailsWithErrno(ENOMEM)); +} + +// Tests for a specific gVisor MM caching bug. +TEST_F(MMapTest, AccessCOWInvalidatesCachedSegments) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); + auto zero_fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); + + // Get a two-page private mapping and fill it with 1s. + uintptr_t addr; + ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + SyscallSucceeds()); + memset(addr_, 1, 2 * kPageSize); + MaybeSave(); + + // Fork to make the mapping copy-on-write. + pid_t const pid = fork(); + if (pid == 0) { + // The child process waits for the parent to SIGKILL it. + while (true) { + pause(); + } + } + ASSERT_THAT(pid, SyscallSucceeds()); + auto cleanup_child = Cleanup([&] { + EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds()); + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + }); + + // Induce a read-only Access of the first page of the mapping, which will not + // cause a copy. The usermem.Segment should be cached. + ASSERT_THAT(PwriteFd(fd.get(), addr_, kPageSize, 0), + SyscallSucceedsWithValue(kPageSize)); + + // Induce a writable Access of both pages of the mapping. This should + // invalidate the cached Segment. + ASSERT_THAT(PreadFd(zero_fd.get(), addr_, 2 * kPageSize, 0), + SyscallSucceedsWithValue(2 * kPageSize)); + + // Induce a read-only Access of the first page of the mapping again. It should + // read the 0s that were stored in the mapping by the read from /dev/zero. If + // the read failed to invalidate the cached Segment, it will instead read the + // 1s in the stale page. + ASSERT_THAT(PwriteFd(fd.get(), addr_, kPageSize, 0), + SyscallSucceedsWithValue(kPageSize)); + std::vector buf(kPageSize); + ASSERT_THAT(PreadFd(fd.get(), buf.data(), kPageSize, 0), + SyscallSucceedsWithValue(kPageSize)); + for (size_t i = 0; i < kPageSize; i++) { + ASSERT_EQ(0, buf[i]) << "at offset " << i; + } +} + +TEST_F(MMapTest, NoReserve) { + const size_t kSize = 10 * 1 << 20; // 10M + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0), + SyscallSucceeds()); + EXPECT_GT(addr, 0); + + // Check that every page can be read/written. Technically, writing to memory + // could SIGSEGV in case there is no more memory available. In gVisor it + // would never happen though because NORESERVE is ignored. In Linux, it's + // possible to fail, but allocation is small enough that it's highly likely + // to succeed. + for (size_t j = 0; j < kSize; j += kPageSize) { + EXPECT_EQ(0, reinterpret_cast(addr)[j]); + reinterpret_cast(addr)[j] = j; + } +} + +// Map more than the gVisor page-cache map unit (64k) and ensure that +// it is consistent with reading from the file. +TEST_F(MMapFileTest, Bug38498194) { + // Choose a sufficiently large map unit. + constexpr int kSize = 4 * 1024 * 1024; + EXPECT_THAT(ftruncate(fd_.get(), kSize), SyscallSucceeds()); + + // Map a large enough region so that multiple internal segments + // are created to back the mapping. + uintptr_t addr; + ASSERT_THAT( + addr = Map(0, kSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + + std::vector expect(kSize, 'a'); + std::copy(expect.data(), expect.data() + expect.size(), + reinterpret_cast(addr)); + + // Trigger writeback for gVisor. In Linux pages stay cached until + // it can't hold onto them anymore. + ASSERT_THAT(Unmap(), SyscallSucceeds()); + + std::vector buf(kSize); + ASSERT_THAT(Read(buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + EXPECT_EQ(buf, expect) << std::string(buf.data(), buf.size()); +} + +// Tests that reading from a file to a memory mapping of the same file does not +// deadlock. +TEST_F(MMapFileTest, SelfRead) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_.get(), 0), + SyscallSucceeds()); + EXPECT_THAT(Read(reinterpret_cast(addr), kPageSize / 2), + SyscallSucceedsWithValue(kPageSize / 2)); + // The resulting file contents are poorly-specified and irrelevant. +} + +// Tests that writing to a file from a memory mapping of the same file does not +// deadlock. +TEST_F(MMapFileTest, SelfWrite) { + uintptr_t addr; + ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), + SyscallSucceeds()); + EXPECT_THAT(Write(reinterpret_cast(addr), kPageSize / 2), + SyscallSucceedsWithValue(kPageSize / 2)); + // The resulting file contents are poorly-specified and irrelevant. +} + +TEST(MMapDeathTest, TruncateAfterCOWBreak) { + SetupGvisorDeathTest(); + + // Create and map a single-page file. + auto const temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDWR)); + ASSERT_THAT(ftruncate(fd.get(), kPageSize), SyscallSucceeds()); + auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(Mmap( + nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd.get(), 0)); + + // Write to this mapping, causing the page to be copied for write. + memset(mapping.ptr(), 'a', mapping.len()); + MaybeSave(); // Trigger a co-operative save cycle. + + // Truncate the file and expect it to invalidate the copied page. + ASSERT_THAT(ftruncate(fd.get(), 0), SyscallSucceeds()); + EXPECT_EXIT(*reinterpret_cast(mapping.ptr()), + ::testing::KilledBySignal(SIGBUS), ""); +} + +// Conditional on MAP_32BIT. +#ifdef __x86_64__ + +TEST(MMapNoFixtureTest, Map32Bit) { + auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE | MAP_32BIT)); + EXPECT_LT(mapping.addr(), static_cast(1) << 32); + EXPECT_LE(mapping.endaddr(), static_cast(1) << 32); +} + +#endif // defined(__x86_64__) + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc new file mode 100644 index 000000000..76da8b75a --- /dev/null +++ b/test/syscalls/linux/mount.cc @@ -0,0 +1,302 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/mount_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(MountTest, MountBadFilesystem) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + // Linux expects a valid target before it checks the file system name. + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(mount("", dir.path().c_str(), "foobar", 0, ""), + SyscallFailsWithErrno(ENODEV)); +} + +TEST(MountTest, MountInvalidTarget) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = NewTempAbsPath(); + EXPECT_THAT(mount("", dir.c_str(), "tmpfs", 0, ""), + SyscallFailsWithErrno(ENOENT)); +} + +TEST(MountTest, MountPermDenied) { + // Clear CAP_SYS_ADMIN. + if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))) { + EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); + } + + // Linux expects a valid target before checking capability. + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(mount("", dir.path().c_str(), "", 0, ""), + SyscallFailsWithErrno(EPERM)); +} + +TEST(MountTest, UmountPermDenied) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const mount = + ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "", 0)); + + // Drop privileges in another thread, so we can still unmount the mounted + // directory. + ScopedThread([&]() { + EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); + EXPECT_THAT(umount(dir.path().c_str()), SyscallFailsWithErrno(EPERM)); + }); +} + +TEST(MountTest, MountOverBusy) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(dir.path(), "foo"), O_CREAT | O_RDWR, 0777)); + + // Should be able to mount over a busy directory. + ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "", 0)); +} + +TEST(MountTest, OpenFileBusy) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const mount = ASSERT_NO_ERRNO_AND_VALUE( + Mount("", dir.path(), "tmpfs", 0, "mode=0700", 0)); + auto const fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(dir.path(), "foo"), O_CREAT | O_RDWR, 0777)); + + // An open file should prevent unmounting. + EXPECT_THAT(umount(dir.path().c_str()), SyscallFailsWithErrno(EBUSY)); +} + +TEST(MountTest, UmountDetach) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + // structure: + // + // dir (mount point) + // subdir + // file + // + // We show that we can walk around in the mount after detach-unmount dir. + // + // We show that even though dir is unreachable from outside the mount, we can + // still reach dir's (former) parent! + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + const struct stat before = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); + auto mount = + ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "mode=0700", + /* umountflags= */ MNT_DETACH)); + const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); + EXPECT_NE(before.st_ino, after.st_ino); + + // Create files in the new mount. + constexpr char kContents[] = "no no no"; + auto const subdir = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); + auto const file = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateFileWith(dir.path(), kContents, 0777)); + + auto const dir_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(subdir.path(), O_RDONLY | O_DIRECTORY)); + auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + // Unmount the tmpfs. + mount.Release()(); + + const struct stat after2 = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); + EXPECT_EQ(before.st_ino, after2.st_ino); + + // Can still read file after unmounting. + std::vector buf(sizeof(kContents)); + EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), SyscallSucceeds()); + + // Walk to dir. + auto const mounted_dir = ASSERT_NO_ERRNO_AND_VALUE( + OpenAt(dir_fd.get(), "..", O_DIRECTORY | O_RDONLY)); + // Walk to dir/file. + auto const fd_again = ASSERT_NO_ERRNO_AND_VALUE( + OpenAt(mounted_dir.get(), std::string(Basename(file.path())), O_RDONLY)); + + std::vector buf2(sizeof(kContents)); + EXPECT_THAT(ReadFd(fd_again.get(), buf2.data(), buf2.size()), + SyscallSucceeds()); + EXPECT_EQ(buf, buf2); + + // Walking outside the unmounted realm should still work, too! + auto const dir_parent = ASSERT_NO_ERRNO_AND_VALUE( + OpenAt(mounted_dir.get(), "..", O_DIRECTORY | O_RDONLY)); +} + +TEST(MountTest, ActiveSubmountBusy) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const mount1 = ASSERT_NO_ERRNO_AND_VALUE( + Mount("", dir.path(), "tmpfs", 0, "mode=0700", 0)); + + auto const dir2 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); + auto const mount2 = + ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir2.path(), "tmpfs", 0, "", 0)); + + // Since dir now has an active submount, should not be able to unmount. + EXPECT_THAT(umount(dir.path().c_str()), SyscallFailsWithErrno(EBUSY)); +} + +TEST(MountTest, MountTmpfs) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const struct stat before = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); + + { + auto const mount = ASSERT_NO_ERRNO_AND_VALUE( + Mount("", dir.path(), "tmpfs", 0, "mode=0700", 0)); + + const struct stat s = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); + EXPECT_EQ(s.st_mode, S_IFDIR | 0700); + EXPECT_NE(s.st_ino, before.st_ino); + + EXPECT_NO_ERRNO(Open(JoinPath(dir.path(), "foo"), O_CREAT | O_RDWR, 0777)); + } + + // Now that dir is unmounted again, we should have the old inode back. + const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); + EXPECT_EQ(before.st_ino, after.st_ino); +} + +TEST(MountTest, MountTmpfsMagicValIgnored) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + auto const mount = ASSERT_NO_ERRNO_AND_VALUE( + Mount("", dir.path(), "tmpfs", MS_MGC_VAL, "mode=0700", 0)); +} + +// Passing nullptr to data is equivalent to "". +TEST(MountTest, NullData) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + EXPECT_THAT(mount("", dir.path().c_str(), "tmpfs", 0, nullptr), + SyscallSucceeds()); + EXPECT_THAT(umount2(dir.path().c_str(), 0), SyscallSucceeds()); +} + +TEST(MountTest, MountReadonly) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const mount = ASSERT_NO_ERRNO_AND_VALUE( + Mount("", dir.path(), "tmpfs", MS_RDONLY, "mode=0777", 0)); + + const struct stat s = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); + EXPECT_EQ(s.st_mode, S_IFDIR | 0777); + + std::string const filename = JoinPath(dir.path(), "foo"); + EXPECT_THAT(open(filename.c_str(), O_RDWR | O_CREAT, 0777), + SyscallFailsWithErrno(EROFS)); +} + +PosixErrorOr ATime(absl::string_view file) { + struct stat s = {}; + if (stat(std::string(file).c_str(), &s) == -1) { + return PosixError(errno, "stat failed"); + } + return absl::TimeFromTimespec(s.st_atim); +} + +// FIXME: Disabled until tmpfs stops using Handle, as only the gofer +// and host file system respect the MS_NOATIME flag. +TEST(MountTest, DISABLED_MountNoAtime) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const mount = ASSERT_NO_ERRNO_AND_VALUE( + Mount("", dir.path(), "tmpfs", MS_NOATIME, "mode=0777", 0)); + + std::string const contents = "No no no, don't follow the instructions!"; + auto const file = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateFileWith(dir.path(), contents, 0777)); + + absl::Time const before = ASSERT_NO_ERRNO_AND_VALUE(ATime(file.path())); + + // Reading from the file should change the atime, but the MS_NOATIME flag + // should prevent that. + auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); + char buf[100]; + int read_n; + ASSERT_THAT(read_n = read(fd.get(), buf, sizeof(buf)), SyscallSucceeds()); + EXPECT_EQ(std::string(buf, read_n), contents); + + absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(ATime(file.path())); + + // Expect that atime hasn't changed. + EXPECT_EQ(before, after); +} + +TEST(MountTest, RenameRemoveMountPoint) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir_parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const dir = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir_parent.path())); + auto const new_dir = NewTempAbsPath(); + + auto const mount = + ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "", 0)); + + ASSERT_THAT(rename(dir.path().c_str(), new_dir.c_str()), + SyscallFailsWithErrno(EBUSY)); + + ASSERT_THAT(rmdir(dir.path().c_str()), SyscallFailsWithErrno(EBUSY)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/mremap.cc b/test/syscalls/linux/mremap.cc new file mode 100644 index 000000000..ededab336 --- /dev/null +++ b/test/syscalls/linux/mremap.cc @@ -0,0 +1,514 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include + +#include "gmock/gmock.h" +#include "absl/strings/string_view.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/memory_util.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +using ::testing::_; + +namespace gvisor { +namespace testing { + +namespace { + +// Wrapper for mremap that returns a PosixErrorOr<>, since the return type of +// void* isn't directly compatible with SyscallSucceeds. +PosixErrorOr Mremap(void* old_address, size_t old_size, size_t new_size, + int flags, void* new_address) { + void* rv = mremap(old_address, old_size, new_size, flags, new_address); + if (rv == MAP_FAILED) { + return PosixError(errno, "mremap failed"); + } + return rv; +} + +// Returns true if the page containing addr is mapped. +bool IsMapped(uintptr_t addr) { + int const rv = msync(reinterpret_cast(addr & ~(kPageSize - 1)), + kPageSize, MS_ASYNC); + if (rv == 0) { + return true; + } + TEST_PCHECK_MSG(errno == ENOMEM, "msync failed with unexpected errno"); + return false; +} + +// Fixture for mremap tests parameterized by mmap flags. +using MremapParamTest = ::testing::TestWithParam; + +TEST_P(MremapParamTest, Noop) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); + + ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize, 0, nullptr), + IsPosixErrorOkAndHolds(m.ptr())); + EXPECT_TRUE(IsMapped(m.addr())); +} + +TEST_P(MremapParamTest, InPlace_ShrinkingWholeVMA) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // N.B. we must be in a single-threaded subprocess to ensure a + // background thread doesn't concurrently map the second page. + void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == m.ptr()); + MaybeSave(); + + TEST_CHECK(IsMapped(m.addr())); + TEST_CHECK(!IsMapped(m.addr() + kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, InPlace_ShrinkingPartialVMA) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == m.ptr()); + MaybeSave(); + + TEST_CHECK(IsMapped(m.addr())); + TEST_CHECK(!IsMapped(m.addr() + kPageSize)); + TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, InPlace_ShrinkingAcrossVMAs) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam())); + // Changing permissions on the first page forces it to become a separate vma. + ASSERT_THAT(mprotect(m.ptr(), kPageSize, PROT_NONE), SyscallSucceeds()); + + const auto rest = [&] { + // Both old_size and new_size now span two vmas; mremap + // shouldn't care. + void* addr = mremap(m.ptr(), 3 * kPageSize, 2 * kPageSize, 0, nullptr); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == m.ptr()); + MaybeSave(); + + TEST_CHECK(IsMapped(m.addr())); + TEST_CHECK(IsMapped(m.addr() + kPageSize)); + TEST_CHECK(!IsMapped(m.addr() + 2 * kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, InPlace_ExpansionSuccess) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unmap the second page so that the first can be expanded back into it. + // + // N.B. we must be in a single-threaded subprocess to ensure a + // background thread doesn't concurrently map this page. + TEST_PCHECK( + munmap(reinterpret_cast(m.addr() + kPageSize), kPageSize) == 0); + MaybeSave(); + + void* addr = mremap(m.ptr(), kPageSize, 2 * kPageSize, 0, nullptr); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == m.ptr()); + MaybeSave(); + + TEST_CHECK(IsMapped(m.addr())); + TEST_CHECK(IsMapped(m.addr() + kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, InPlace_ExpansionFailure) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unmap the second page, leaving a one-page hole. Trying to expand the + // first page to three pages should fail since the original third page + // is still mapped. + TEST_PCHECK( + munmap(reinterpret_cast(m.addr() + kPageSize), kPageSize) == 0); + MaybeSave(); + + void* addr = mremap(m.ptr(), kPageSize, 3 * kPageSize, 0, nullptr); + TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded"); + TEST_PCHECK_MSG(errno == ENOMEM, "mremap failed with wrong errno"); + MaybeSave(); + + TEST_CHECK(IsMapped(m.addr())); + TEST_CHECK(!IsMapped(m.addr() + kPageSize)); + TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, MayMove_Expansion) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unmap the second page, leaving a one-page hole. Trying to expand the + // first page to three pages with MREMAP_MAYMOVE should force the + // mapping to be relocated since the original third page is still + // mapped. + TEST_PCHECK( + munmap(reinterpret_cast(m.addr() + kPageSize), kPageSize) == 0); + MaybeSave(); + + void* addr2 = + mremap(m.ptr(), kPageSize, 3 * kPageSize, MREMAP_MAYMOVE, nullptr); + TEST_PCHECK_MSG(addr2 != MAP_FAILED, "mremap failed"); + MaybeSave(); + + const Mapping m2 = Mapping(addr2, 3 * kPageSize); + TEST_CHECK(m.addr() != m2.addr()); + + TEST_CHECK(!IsMapped(m.addr())); + TEST_CHECK(!IsMapped(m.addr() + kPageSize)); + TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize)); + TEST_CHECK(IsMapped(m2.addr())); + TEST_CHECK(IsMapped(m2.addr() + kPageSize)); + TEST_CHECK(IsMapped(m2.addr() + 2 * kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, Fixed_SourceAndDestinationCannotOverlap) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); + + ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, m.ptr()), + PosixErrorIs(EINVAL, _)); + EXPECT_TRUE(IsMapped(m.addr())); +} + +TEST_P(MremapParamTest, Fixed_SameSize) { + Mapping const src = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); + Mapping const dst = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unmap dst to create a hole. + TEST_PCHECK(munmap(dst.ptr(), kPageSize) == 0); + MaybeSave(); + + void* addr = mremap(src.ptr(), kPageSize, kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == dst.ptr()); + MaybeSave(); + + TEST_CHECK(!IsMapped(src.addr())); + TEST_CHECK(IsMapped(dst.addr())); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, Fixed_SameSize_Unmapping) { + // Like the Fixed_SameSize case, but expect mremap to unmap the destination + // automatically. + Mapping const src = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); + Mapping const dst = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + void* addr = mremap(src.ptr(), kPageSize, kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == dst.ptr()); + MaybeSave(); + + TEST_CHECK(!IsMapped(src.addr())); + TEST_CHECK(IsMapped(dst.addr())); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, Fixed_ShrinkingWholeVMA) { + Mapping const src = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); + Mapping const dst = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unmap dst so we can check that mremap does not keep the + // second page. + TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0); + MaybeSave(); + + void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == dst.ptr()); + MaybeSave(); + + TEST_CHECK(!IsMapped(src.addr())); + TEST_CHECK(!IsMapped(src.addr() + kPageSize)); + TEST_CHECK(IsMapped(dst.addr())); + TEST_CHECK(!IsMapped(dst.addr() + kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, Fixed_ShrinkingPartialVMA) { + Mapping const src = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); + Mapping const dst = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unmap dst so we can check that mremap does not keep the + // second page. + TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0); + MaybeSave(); + + void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == dst.ptr()); + MaybeSave(); + + TEST_CHECK(!IsMapped(src.addr())); + TEST_CHECK(!IsMapped(src.addr() + kPageSize)); + TEST_CHECK(IsMapped(src.addr() + 2 * kPageSize)); + TEST_CHECK(IsMapped(dst.addr())); + TEST_CHECK(!IsMapped(dst.addr() + kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, Fixed_ShrinkingAcrossVMAs) { + Mapping const src = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam())); + // Changing permissions on the first page forces it to become a separate vma. + ASSERT_THAT(mprotect(src.ptr(), kPageSize, PROT_NONE), SyscallSucceeds()); + Mapping const dst = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unlike flags=0, MREMAP_FIXED requires that [old_address, + // old_address+new_size) only spans a single vma. + void* addr = mremap(src.ptr(), 3 * kPageSize, 2 * kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); + TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded"); + TEST_PCHECK_MSG(errno == EFAULT, "mremap failed with wrong errno"); + MaybeSave(); + + TEST_CHECK(IsMapped(src.addr())); + TEST_CHECK(IsMapped(src.addr() + kPageSize)); + // Despite failing, mremap should have unmapped [old_address+new_size, + // old_address+old_size) (i.e. the third page). + TEST_CHECK(!IsMapped(src.addr() + 2 * kPageSize)); + // Despite failing, mremap should have unmapped the destination pages. + TEST_CHECK(!IsMapped(dst.addr())); + TEST_CHECK(!IsMapped(dst.addr() + kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST_P(MremapParamTest, Fixed_Expansion) { + Mapping const src = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); + Mapping const dst = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); + + const auto rest = [&] { + // Unmap dst so we can check that mremap actually maps all pages + // at the destination. + TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0); + MaybeSave(); + + void* addr = mremap(src.ptr(), kPageSize, 2 * kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); + TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); + TEST_CHECK(addr == dst.ptr()); + MaybeSave(); + + TEST_CHECK(!IsMapped(src.addr())); + TEST_CHECK(IsMapped(dst.addr())); + TEST_CHECK(IsMapped(dst.addr() + kPageSize)); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +INSTANTIATE_TEST_CASE_P(PrivateShared, MremapParamTest, + ::testing::Values(MAP_PRIVATE, MAP_SHARED)); + +// mremap with old_size == 0 only works with MAP_SHARED after Linux 4.14 +// (dba58d3b8c50 "mm/mremap: fail map duplication attempts for private +// mappings"). + +TEST(MremapTest, InPlace_Copy) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED)); + EXPECT_THAT(Mremap(m.ptr(), 0, kPageSize, 0, nullptr), + PosixErrorIs(ENOMEM, _)); +} + +TEST(MremapTest, MayMove_Copy) { + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED)); + + // Remainder of this test executes in a subprocess to ensure that if mremap + // incorrectly removes m, it is not remapped by another thread. + const auto rest = [&] { + void* ptr = mremap(m.ptr(), 0, kPageSize, MREMAP_MAYMOVE, nullptr); + MaybeSave(); + TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed"); + TEST_CHECK(ptr != m.ptr()); + TEST_CHECK(IsMapped(m.addr())); + TEST_CHECK(IsMapped(reinterpret_cast(ptr))); + }; + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +TEST(MremapTest, MustMove_Copy) { + Mapping const src = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED)); + Mapping const dst = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); + + // Remainder of this test executes in a subprocess to ensure that if mremap + // incorrectly removes src, it is not remapped by another thread. + const auto rest = [&] { + void* ptr = mremap(src.ptr(), 0, kPageSize, MREMAP_MAYMOVE | MREMAP_FIXED, + dst.ptr()); + MaybeSave(); + TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed"); + TEST_CHECK(ptr == dst.ptr()); + TEST_CHECK(IsMapped(src.addr())); + TEST_CHECK(IsMapped(dst.addr())); + }; + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); +} + +void ExpectAllBytesAre(absl::string_view v, char c) { + for (size_t i = 0; i < v.size(); i++) { + ASSERT_EQ(v[i], c) << "at offset " << i; + } +} + +TEST(MremapTest, ExpansionPreservesCOWPagesAndExposesNewFilePages) { + // Create a file with 3 pages. The first is filled with 'a', the second is + // filled with 'b', and the third is filled with 'c'. + TempPath const file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); + ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'a').c_str(), kPageSize), + SyscallSucceedsWithValue(kPageSize)); + ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'b').c_str(), kPageSize), + SyscallSucceedsWithValue(kPageSize)); + ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'c').c_str(), kPageSize), + SyscallSucceedsWithValue(kPageSize)); + + // Create a private mapping of the first 2 pages, and fill the second page + // with 'd'. + Mapping const src = ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, 2 * kPageSize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, fd.get(), 0)); + memset(reinterpret_cast(src.addr() + kPageSize), 'd', kPageSize); + MaybeSave(); + + // Move the mapping while expanding it to 3 pages. The resulting mapping + // should contain the original first page of the file (filled with 'a'), + // followed by the private copy of the second page (filled with 'd'), followed + // by the newly-mapped third page of the file (filled with 'c'). + Mapping const dst = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(3 * kPageSize, PROT_NONE, MAP_PRIVATE)); + ASSERT_THAT(Mremap(src.ptr(), 2 * kPageSize, 3 * kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()), + IsPosixErrorOkAndHolds(dst.ptr())); + auto const v = dst.view(); + ExpectAllBytesAre(v.substr(0, kPageSize), 'a'); + ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'd'); + ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), 'c'); +} + +TEST(MremapDeathTest, SharedAnon) { + SetupGvisorDeathTest(); + + // Reserve 4 pages of address space. + Mapping const reserved = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(4 * kPageSize, PROT_NONE, MAP_PRIVATE)); + + // Create a 2-page shared anonymous mapping at the beginning of the + // reservation. Fill the first page with 'a' and the second with 'b'. + Mapping const m = ASSERT_NO_ERRNO_AND_VALUE( + Mmap(reserved.ptr(), 2 * kPageSize, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0)); + memset(m.ptr(), 'a', kPageSize); + memset(reinterpret_cast(m.addr() + kPageSize), 'b', kPageSize); + MaybeSave(); + + // Shrink the mapping to 1 page in-place. + ASSERT_THAT(Mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, m.ptr()), + IsPosixErrorOkAndHolds(m.ptr())); + + // Expand the mapping to 3 pages, moving it forward by 1 page in the process + // since the old and new mappings can't overlap. + void* const new_m = reinterpret_cast(m.addr() + kPageSize); + ASSERT_THAT(Mremap(m.ptr(), kPageSize, 3 * kPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, new_m), + IsPosixErrorOkAndHolds(new_m)); + + // The first 2 pages of the mapping should still contain the data we wrote + // (i.e. shrinking should not have discarded the second page's data), while + // touching the third page should raise SIGBUS. + auto const v = + absl::string_view(static_cast(new_m), 3 * kPageSize); + ExpectAllBytesAre(v.substr(0, kPageSize), 'a'); + ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'b'); + EXPECT_EXIT(ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), '\0'), + ::testing::KilledBySignal(SIGBUS), ""); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/msync.cc b/test/syscalls/linux/msync.cc new file mode 100644 index 000000000..0ddc621aa --- /dev/null +++ b/test/syscalls/linux/msync.cc @@ -0,0 +1,145 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include +#include +#include +#include + +#include "test/util/file_descriptor.h" +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Parameters for msync tests. Use a std::tuple so we can use +// ::testing::Combine. +using MsyncTestParam = + std::tuple()> // returns mapping to + // msync + >; + +class MsyncParameterizedTest : public ::testing::TestWithParam { + protected: + int msync_flags() const { return std::get<0>(GetParam()); } + + PosixErrorOr GetMapping() const { + auto rv = std::get<1>(GetParam())(); + return rv; + } +}; + +// All valid msync(2) flag combinations (not including MS_INVALIDATE, which +// gVisor doesn't implement). +constexpr std::initializer_list kMsyncFlags = {MS_SYNC, MS_ASYNC, 0}; + +// Returns functions that return mappings that should be successfully +// msync()able. +std::vector()>> SyncableMappings() { + std::vector()>> funcs; + for (bool const writable : {false, true}) { + for (int const mflags : {MAP_PRIVATE, MAP_SHARED}) { + int const prot = PROT_READ | (writable ? PROT_WRITE : 0); + int const oflags = O_CREAT | (writable ? O_RDWR : O_RDONLY); + funcs.push_back([=] { + return MmapAnon(kPageSize, prot, mflags); + }); + funcs.push_back([=]() -> PosixErrorOr { + std::string const path = NewTempAbsPath(); + ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, oflags, 0644)); + // Don't unlink the file since that breaks save/restore. Just let the + // test infrastructure clean up all of our temporary files when we're + // done. + return Mmap(nullptr, kPageSize, prot, mflags, fd.get(), 0); + }); + } + } + return funcs; +} + +PosixErrorOr NoMappings() { + return PosixError(EINVAL, "unexpected attempt to create a mapping"); +} + +// "Fixture" for msync tests that hold for all valid flags, but do not create +// mappings. +using MsyncNoMappingTest = MsyncParameterizedTest; + +TEST_P(MsyncNoMappingTest, UnmappedAddressWithZeroLengthSucceeds) { + EXPECT_THAT(msync(nullptr, 0, msync_flags()), SyscallSucceeds()); +} + +TEST_P(MsyncNoMappingTest, UnmappedAddressWithNonzeroLengthFails) { + EXPECT_THAT(msync(nullptr, kPageSize, msync_flags()), + SyscallFailsWithErrno(ENOMEM)); +} + +INSTANTIATE_TEST_CASE_P(All, MsyncNoMappingTest, + ::testing::Combine(::testing::ValuesIn(kMsyncFlags), + ::testing::Values(NoMappings))); + +// "Fixture" for msync tests that are not parameterized by msync flags, but do +// create mappings. +using MsyncNoFlagsTest = MsyncParameterizedTest; + +TEST_P(MsyncNoFlagsTest, BothSyncAndAsyncFails) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT(msync(m.ptr(), m.len(), MS_SYNC | MS_ASYNC), + SyscallFailsWithErrno(EINVAL)); +} + +INSTANTIATE_TEST_CASE_P( + All, MsyncNoFlagsTest, + ::testing::Combine(::testing::Values(0), // ignored + ::testing::ValuesIn(SyncableMappings()))); + +// "Fixture" for msync tests parameterized by both msync flags and sources of +// mappings. +using MsyncFullParamTest = MsyncParameterizedTest; + +TEST_P(MsyncFullParamTest, NormallySucceeds) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT(msync(m.ptr(), m.len(), msync_flags()), SyscallSucceeds()); +} + +TEST_P(MsyncFullParamTest, UnalignedLengthSucceeds) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT(msync(m.ptr(), m.len() - 1, msync_flags()), SyscallSucceeds()); +} + +TEST_P(MsyncFullParamTest, UnalignedAddressFails) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT( + msync(reinterpret_cast(m.addr() + 1), m.len() - 1, msync_flags()), + SyscallFailsWithErrno(EINVAL)); +} + +INSTANTIATE_TEST_CASE_P( + All, MsyncFullParamTest, + ::testing::Combine(::testing::ValuesIn(kMsyncFlags), + ::testing::ValuesIn(SyncableMappings()))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/munmap.cc b/test/syscalls/linux/munmap.cc new file mode 100644 index 000000000..e20039950 --- /dev/null +++ b/test/syscalls/linux/munmap.cc @@ -0,0 +1,53 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class MunmapTest : public ::testing::Test { + protected: + void SetUp() override { + m_ = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, m_); + } + + void* m_ = nullptr; +}; + +TEST_F(MunmapTest, HappyCase) { + EXPECT_THAT(munmap(m_, kPageSize), SyscallSucceeds()); +} + +TEST_F(MunmapTest, ZeroLength) { + EXPECT_THAT(munmap(m_, 0), SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(MunmapTest, LastPageRoundUp) { + // Attempt to unmap up to and including the last page. + EXPECT_THAT(munmap(m_, static_cast(-kPageSize + 1)), + SyscallFailsWithErrno(EINVAL)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc new file mode 100644 index 000000000..5770680cd --- /dev/null +++ b/test/syscalls/linux/open.cc @@ -0,0 +1,340 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/file_base.h" +#include "test/util/capability_util.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// This test is currently very rudimentary. +// +// There are plenty of extra cases to cover once the sentry supports them. +// +// Different types of opens: +// * O_CREAT +// * O_DIRECTORY +// * O_NOFOLLOW +// * O_PATH <- Will we ever support this? +// +// Special operations on open: +// * O_EXCL +// +// Special files: +// * Blocking behavior for a named pipe. +// +// Different errors: +// * EACCES +// * EEXIST +// * ENAMETOOLONG +// * ELOOP +// * ENOTDIR +// * EPERM +class OpenTest : public FileTest { + void SetUp() override { + FileTest::SetUp(); + + ASSERT_THAT( + write(test_file_fd_.get(), test_data_.c_str(), test_data_.length()), + SyscallSucceedsWithValue(test_data_.length())); + EXPECT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), SyscallSucceeds()); + } + + public: + const std::string test_data_ = "hello world\n"; +}; + +TEST_F(OpenTest, ReadOnly) { + char buf; + const FileDescriptor ro_file = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); + + EXPECT_THAT(read(ro_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_THAT(lseek(ro_file.get(), 0, SEEK_SET), SyscallSucceeds()); + EXPECT_THAT(write(ro_file.get(), &buf, 1), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(OpenTest, WriteOnly) { + char buf; + const FileDescriptor wo_file = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY)); + + EXPECT_THAT(read(wo_file.get(), &buf, 1), SyscallFailsWithErrno(EBADF)); + EXPECT_THAT(lseek(wo_file.get(), 0, SEEK_SET), SyscallSucceeds()); + EXPECT_THAT(write(wo_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); +} + +TEST_F(OpenTest, ReadWrite) { + char buf; + const FileDescriptor rw_file = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + EXPECT_THAT(read(rw_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); + EXPECT_THAT(lseek(rw_file.get(), 0, SEEK_SET), SyscallSucceeds()); + EXPECT_THAT(write(rw_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); +} + +TEST_F(OpenTest, RelPath) { + auto name = std::string(Basename(test_file_name_)); + + ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name, O_RDONLY)); +} + +TEST_F(OpenTest, AbsPath) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); +} + +TEST_F(OpenTest, AtRelPath) { + auto name = std::string(Basename(test_file_name_)); + const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( + Open(GetAbsoluteTestTmpdir(), O_RDONLY | O_DIRECTORY)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(OpenAt(dirfd.get(), name, O_RDONLY)); +} + +TEST_F(OpenTest, AtAbsPath) { + const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( + Open(GetAbsoluteTestTmpdir(), O_RDONLY | O_DIRECTORY)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(OpenAt(dirfd.get(), test_file_name_, O_RDONLY)); +} + +TEST_F(OpenTest, OpenNoFollowSymlink) { + const std::string link_path = JoinPath(GetAbsoluteTestTmpdir(), "link"); + ASSERT_THAT(symlink(test_file_name_.c_str(), link_path.c_str()), + SyscallSucceeds()); + auto cleanup = Cleanup([link_path]() { + EXPECT_THAT(unlink(link_path.c_str()), SyscallSucceeds()); + }); + + // Open will succeed without O_NOFOLLOW and fails with O_NOFOLLOW. + const FileDescriptor fd2 = + ASSERT_NO_ERRNO_AND_VALUE(Open(link_path, O_RDONLY)); + ASSERT_THAT(open(link_path.c_str(), O_RDONLY | O_NOFOLLOW), + SyscallFailsWithErrno(ELOOP)); +} + +TEST_F(OpenTest, OpenNoFollowStillFollowsLinksInPath) { + // We will create the following structure: + // tmp_folder/real_folder/file + // tmp_folder/sym_folder -> tmp_folder/real_folder + // + // We will then open tmp_folder/sym_folder/file with O_NOFOLLOW and it + // should succeed as O_NOFOLLOW only applies to the final path component. + auto tmp_path = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(GetAbsoluteTestTmpdir())); + auto sym_path = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), tmp_path.path())); + auto file_path = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmp_path.path())); + + auto path_via_symlink = JoinPath(sym_path.path(), Basename(file_path.path())); + const FileDescriptor fd2 = + ASSERT_NO_ERRNO_AND_VALUE(Open(path_via_symlink, O_RDONLY | O_NOFOLLOW)); +} + +TEST_F(OpenTest, Fault) { + char* totally_not_null = nullptr; + ASSERT_THAT(open(totally_not_null, O_RDONLY), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(OpenTest, AppendOnly) { + // First write some data to the fresh file. + const int64_t kBufSize = 1024; + std::vector buf(kBufSize, 'a'); + + FileDescriptor fd0 = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); + + std::fill(buf.begin(), buf.end(), 'a'); + EXPECT_THAT(WriteFd(fd0.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + fd0.reset(); // Close the file early. + + // Next get two handles to the same file. We open two files because we want + // to make sure that appending is respected between them. + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_APPEND)); + EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + + const FileDescriptor fd2 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_APPEND)); + EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + + // Then try to write to the first file and make sure the bytes are appended. + EXPECT_THAT(WriteFd(fd1.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + // Check that the size of the file is correct and that the offset has been + // incremented to that size. + struct stat s0; + EXPECT_THAT(fstat(fd1.get(), &s0), SyscallSucceeds()); + EXPECT_EQ(s0.st_size, kBufSize * 2); + EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), + SyscallSucceedsWithValue(kBufSize * 2)); + + // Then try to write to the second file and make sure the bytes are appended. + EXPECT_THAT(WriteFd(fd2.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + // Check that the size of the file is correct and that the offset has been + // incremented to that size. + struct stat s1; + EXPECT_THAT(fstat(fd2.get(), &s1), SyscallSucceeds()); + EXPECT_EQ(s1.st_size, kBufSize * 3); + EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR), + SyscallSucceedsWithValue(kBufSize * 3)); +} + +TEST_F(OpenTest, Truncate) { + { + // First write some data to the new file and close it. + FileDescriptor fd0 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY)); + std::vector orig(10, 'a'); + EXPECT_THAT(WriteFd(fd0.get(), orig.data(), orig.size()), + SyscallSucceedsWithValue(orig.size())); + } + + // Then open with truncate and verify that offset is set to 0. + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_TRUNC)); + EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + + // Then write less data to the file and ensure the old content is gone. + std::vector want(5, 'b'); + EXPECT_THAT(WriteFd(fd1.get(), want.data(), want.size()), + SyscallSucceedsWithValue(want.size())); + + struct stat stat; + EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds()); + EXPECT_EQ(stat.st_size, want.size()); + EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), + SyscallSucceedsWithValue(want.size())); + + // Read the data and ensure only the latest write is in the file. + std::vector got(want.size() + 1, 'c'); + ASSERT_THAT(pread(fd1.get(), got.data(), got.size(), 0), + SyscallSucceedsWithValue(want.size())); + EXPECT_EQ(memcmp(want.data(), got.data(), want.size()), 0) + << "rbuf=" << got.data(); + EXPECT_EQ(got.back(), 'c'); // Last byte should not have been modified. +} + +TEST_F(OpenTest, NameTooLong) { + char buf[4097] = {}; + memset(buf, 'a', 4097); + EXPECT_THAT(open(buf, O_RDONLY), SyscallFailsWithErrno(ENAMETOOLONG)); +} + +TEST_F(OpenTest, DotsFromRoot) { + const FileDescriptor rootfd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/", O_RDONLY | O_DIRECTORY)); + const FileDescriptor other_rootfd = + ASSERT_NO_ERRNO_AND_VALUE(OpenAt(rootfd.get(), "..", O_RDONLY)); +} + +TEST_F(OpenTest, DirectoryWritableFails) { + ASSERT_THAT(open(GetAbsoluteTestTmpdir().c_str(), O_RDWR), + SyscallFailsWithErrno(EISDIR)); +} + +TEST_F(OpenTest, FileNotDirectory) { + // Create a file and try to open it with O_DIRECTORY. + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + ASSERT_THAT(open(file.path().c_str(), O_RDONLY | O_DIRECTORY), + SyscallFailsWithErrno(ENOTDIR)); +} + +TEST_F(OpenTest, Null) { + char c = '\0'; + ASSERT_THAT(open(&c, O_RDONLY), SyscallFailsWithErrno(ENOENT)); +} + +// NOTE: While the man pages specify that this behavior should be +// undefined, Linux truncates the file on opening read only if we have write +// permission, so we will too. +TEST_F(OpenTest, CanTruncateReadOnly) { + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY | O_TRUNC)); + + struct stat stat; + EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds()); + EXPECT_EQ(stat.st_size, 0); +} + +// If we don't have read permission on the file, opening with +// O_TRUNC should fail. +TEST_F(OpenTest, CanTruncateReadOnlyNoWritePermission) { + // Drop capabilities that allow us to override file permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + + const DisableSave ds; // Permissions are dropped. + ASSERT_THAT(chmod(test_file_name_.c_str(), S_IRUSR | S_IRGRP), + SyscallSucceeds()); + + ASSERT_THAT(open(test_file_name_.c_str(), O_RDONLY | O_TRUNC), + SyscallFailsWithErrno(EACCES)); + + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); + + struct stat stat; + EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds()); + EXPECT_EQ(stat.st_size, test_data_.size()); +} + +// If we don't have read permission but have write permission, opening O_WRONLY +// and O_TRUNC should succeed. +TEST_F(OpenTest, CanTruncateWriteOnlyNoReadPermission) { + const DisableSave ds; // Permissions are dropped. + + EXPECT_THAT(fchmod(test_file_fd_.get(), S_IWUSR | S_IWGRP), + SyscallSucceeds()); + + const FileDescriptor fd1 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY | O_TRUNC)); + + EXPECT_THAT(fchmod(test_file_fd_.get(), S_IRUSR | S_IRGRP), + SyscallSucceeds()); + + const FileDescriptor fd2 = + ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); + + struct stat stat; + EXPECT_THAT(fstat(fd2.get(), &stat), SyscallSucceeds()); + EXPECT_EQ(stat.st_size, 0); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/open_create.cc b/test/syscalls/linux/open_create.cc new file mode 100644 index 000000000..b2cbd63d1 --- /dev/null +++ b/test/syscalls/linux/open_create.cc @@ -0,0 +1,130 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/syscalls/linux/temp_umask.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { +TEST(CreateTest, TmpFile) { + int fd; + EXPECT_THAT(fd = open(JoinPath(GetAbsoluteTestTmpdir(), "a").c_str(), + O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(CreateTest, ExistingFile) { + int fd; + EXPECT_THAT( + fd = open(JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile").c_str(), + O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + EXPECT_THAT( + fd = open(JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile").c_str(), + O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(CreateTest, CreateAtFile) { + int dirfd; + EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0666), + SyscallSucceeds()); + EXPECT_THAT(openat(dirfd, "CreateAtFile", O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(dirfd), SyscallSucceeds()); +} + +TEST(CreateTest, HonorsUmask_NoRandomSave) { + const DisableSave ds; // file cannot be re-opened as writable. + TempUmask mask(0222); + int fd; + ASSERT_THAT( + fd = open(JoinPath(GetAbsoluteTestTmpdir(), "UmaskedFile").c_str(), + O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + struct stat statbuf; + ASSERT_THAT(fstat(fd, &statbuf), SyscallSucceeds()); + EXPECT_EQ(0444, statbuf.st_mode & 0777); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(CreateTest, CreateExclusively) { + std::string filename = NewTempAbsPath(); + + int fd; + ASSERT_THAT(fd = open(filename.c_str(), O_CREAT | O_RDWR, 0644), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + EXPECT_THAT(open(filename.c_str(), O_CREAT | O_EXCL | O_RDWR, 0644), + SyscallFailsWithErrno(EEXIST)); +} + +TEST(CreateTest, CreateFailsOnUnpermittedDir) { + // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to + // always override directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_THAT(open("/foo", O_CREAT | O_RDWR, 0644), + SyscallFailsWithErrno(EACCES)); +} + +TEST(CreateTest, CreateFailsOnDirWithoutWritePerms) { + // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to + // always override directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + auto parent = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555)); + auto file = JoinPath(parent.path(), "foo"); + ASSERT_THAT(open(file.c_str(), O_CREAT | O_RDWR, 0644), + SyscallFailsWithErrno(EACCES)); +} + +// A file originally created RW, but opened RO can later be opened RW. +TEST(CreateTest, OpenCreateROThenRW) { + TempPath file(NewTempAbsPath()); + + // Create a RW file, but only open it RO. + FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE( + Open(file.path(), O_CREAT | O_EXCL | O_RDONLY, 0644)); + + // Now get a RW FD. + FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); + + // fd1 is not writable, but fd2 is. + char c = 'a'; + EXPECT_THAT(WriteFd(fd1.get(), &c, 1), SyscallFailsWithErrno(EBADF)); + EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/partial_bad_buffer.cc b/test/syscalls/linux/partial_bad_buffer.cc new file mode 100644 index 000000000..073a6b8c1 --- /dev/null +++ b/test/syscalls/linux/partial_bad_buffer.cc @@ -0,0 +1,305 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +using ::testing::Gt; + +namespace gvisor { +namespace testing { + +namespace { + +constexpr char kMessage[] = "hello world"; + +// PartialBadBufferTest checks the result of various IO syscalls when passed a +// buffer that does not have the space specified in the syscall (most of it is +// PROT_NONE). Linux is annoyingly inconsistent among different syscalls, so we +// test all of them. +class PartialBadBufferTest : public ::testing::Test { + protected: + void SetUp() override { + // Create and open a directory for getdents cases. + directory_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + ASSERT_THAT( + directory_fd_ = open(directory_.path().c_str(), O_RDONLY | O_DIRECTORY), + SyscallSucceeds()); + + // Create and open a normal file, placing it in the directory + // so the getdents cases have some dirents. + name_ = JoinPath(directory_.path(), "a"); + ASSERT_THAT(fd_ = open(name_.c_str(), O_RDWR | O_CREAT, 0644), + SyscallSucceeds()); + + // Write some initial data. + size_t size = sizeof(kMessage) - 1; + EXPECT_THAT(WriteFd(fd_, &kMessage, size), SyscallSucceedsWithValue(size)); + + ASSERT_THAT(lseek(fd_, 0, SEEK_SET), SyscallSucceeds()); + + addr_ = mmap(0, 2 * kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(addr_, MAP_FAILED); + char* buf = reinterpret_cast(addr_); + + // Guard page for our read to run into. + ASSERT_THAT(mprotect(reinterpret_cast(buf + kPageSize), kPageSize, + PROT_NONE), + SyscallSucceeds()); + + // Leave only one free byte in the buffer. + bad_buffer_ = buf + kPageSize - 1; + } + + void TearDown() override { + EXPECT_THAT(munmap(addr_, 2 * kPageSize), SyscallSucceeds()) << addr_; + EXPECT_THAT(close(fd_), SyscallSucceeds()); + EXPECT_THAT(unlink(name_.c_str()), SyscallSucceeds()); + EXPECT_THAT(close(directory_fd_), SyscallSucceeds()); + } + + // Return buffer with n bytes of free space. + // N.B. this is the same buffer used to back bad_buffer_. + char* FreeBytes(size_t n) { + TEST_CHECK(n <= static_cast(4096)); + return reinterpret_cast(addr_) + kPageSize - n; + } + + std::string name_; + int fd_; + TempPath directory_; + int directory_fd_; + void* addr_; + char* bad_buffer_; +}; + +// We do both "big" and "small" tests to try to hit the "zero copy" and +// non-"zero copy" paths, which have different code paths for handling faults. + +TEST_F(PartialBadBufferTest, ReadBig) { + EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, kPageSize), + SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, ReadSmall) { + EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, 10), + SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, PreadBig) { + EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, kPageSize, 0), + SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, PreadSmall) { + EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, 10, 0), + SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, ReadvBig) { + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = kPageSize; + + EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, ReadvSmall) { + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = 10; + + EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, PreadvBig) { + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = kPageSize; + + EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, PreadvSmall) { + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = 10; + + EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1)); + EXPECT_EQ('h', bad_buffer_[0]); +} + +TEST_F(PartialBadBufferTest, WriteBig) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + EXPECT_THAT(RetryEINTR(write)(fd_, bad_buffer_, kPageSize), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(PartialBadBufferTest, WriteSmall) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + EXPECT_THAT(RetryEINTR(write)(fd_, bad_buffer_, 10), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(PartialBadBufferTest, PwriteBig) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + EXPECT_THAT(RetryEINTR(pwrite)(fd_, bad_buffer_, kPageSize, 0), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(PartialBadBufferTest, PwriteSmall) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + EXPECT_THAT(RetryEINTR(pwrite)(fd_, bad_buffer_, 10, 0), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(PartialBadBufferTest, WritevBig) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = kPageSize; + + EXPECT_THAT(RetryEINTR(writev)(fd_, &vec, 1), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(PartialBadBufferTest, WritevSmall) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = 10; + + EXPECT_THAT(RetryEINTR(writev)(fd_, &vec, 1), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(PartialBadBufferTest, PwritevBig) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = kPageSize; + + EXPECT_THAT(RetryEINTR(pwritev)(fd_, &vec, 1, 0), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(PartialBadBufferTest, PwritevSmall) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + struct iovec vec; + vec.iov_base = bad_buffer_; + vec.iov_len = 10; + + EXPECT_THAT(RetryEINTR(pwritev)(fd_, &vec, 1, 0), + SyscallFailsWithErrno(EFAULT)); +} + +// getdents returns EFAULT when the you claim the buffer is large enough, but +// it actually isn't. +TEST_F(PartialBadBufferTest, GetdentsBig) { + EXPECT_THAT(RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_, + kPageSize), + SyscallFailsWithErrno(EFAULT)); +} + +// getdents returns EINVAL when the you claim the buffer is too small. +TEST_F(PartialBadBufferTest, GetdentsSmall) { + EXPECT_THAT( + RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_, 10), + SyscallFailsWithErrno(EINVAL)); +} + +// getdents will write entries into a buffer if there is space before it faults. +TEST_F(PartialBadBufferTest, GetdentsOneEntry) { + // 30 bytes is enough for one (small) entry. + char* buf = FreeBytes(30); + + EXPECT_THAT( + RetryEINTR(syscall)(SYS_getdents64, directory_fd_, buf, kPageSize), + SyscallSucceedsWithValue(Gt(0))); +} + +// Verify that when write returns EFAULT the kernel hasn't silently written +// the initial valid bytes. +TEST_F(PartialBadBufferTest, WriteEfaultIsntPartial) { + // FIXME: The sentry write syscalls will return immediately + // if Access returns an error, but Access may not return an error + // and the sentry will instead perform a partial write. + SKIP_IF(IsRunningOnGvisor()); + + bad_buffer_[0] = 'A'; + EXPECT_THAT(RetryEINTR(write)(fd_, bad_buffer_, 10), + SyscallFailsWithErrno(EFAULT)); + + size_t size = 255; + char buf[255]; + memset(buf, 0, size); + + EXPECT_THAT(RetryEINTR(pread)(fd_, buf, size, 0), + SyscallSucceedsWithValue(sizeof(kMessage) - 1)); + + // 'A' has not been written. + EXPECT_STREQ(buf, kMessage); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/pause.cc b/test/syscalls/linux/pause.cc new file mode 100644 index 000000000..4e1148c24 --- /dev/null +++ b/test/syscalls/linux/pause.cc @@ -0,0 +1,88 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +void NoopSignalHandler(int sig, siginfo_t* info, void* context) {} + +} // namespace + +TEST(PauseTest, OnlyReturnsWhenSignalHandled) { + struct sigaction sa; + sigfillset(&sa.sa_mask); + + // Ensure that SIGUSR1 is ignored. + sa.sa_handler = SIG_IGN; + ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); + + // Register a handler for SIGUSR2. + sa.sa_sigaction = NoopSignalHandler; + sa.sa_flags = SA_SIGINFO; + ASSERT_THAT(sigaction(SIGUSR2, &sa, nullptr), SyscallSucceeds()); + + // The child sets their own tid. + absl::Mutex mu; + pid_t child_tid = 0; + bool child_tid_available = false; + std::atomic sent_signal{0}; + std::atomic waking_signal{0}; + ScopedThread t([&] { + mu.Lock(); + child_tid = gettid(); + child_tid_available = true; + mu.Unlock(); + EXPECT_THAT(pause(), SyscallFailsWithErrno(EINTR)); + waking_signal.store(sent_signal.load()); + }); + mu.Lock(); + mu.Await(absl::Condition(&child_tid_available)); + mu.Unlock(); + + // Wait a bit to let the child enter pause(). + absl::SleepFor(absl::Seconds(3)); + + // The child should not be woken by SIGUSR1. + sent_signal.store(SIGUSR1); + ASSERT_THAT(tgkill(getpid(), child_tid, SIGUSR1), SyscallSucceeds()); + absl::SleepFor(absl::Seconds(3)); + + // The child should be woken by SIGUSR2. + sent_signal.store(SIGUSR2); + ASSERT_THAT(tgkill(getpid(), child_tid, SIGUSR2), SyscallSucceeds()); + absl::SleepFor(absl::Seconds(3)); + + EXPECT_EQ(SIGUSR2, waking_signal.load()); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc new file mode 100644 index 000000000..4731157e8 --- /dev/null +++ b/test/syscalls/linux/pipe.cc @@ -0,0 +1,480 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include /* Obtain O_* constant definitions */ +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Buffer size of a pipe. +// +// TODO: Get this from F_GETPIPE_SZ. +constexpr int kPipeSize = 65536; + +class PipeTest : public ::testing::Test { + public: + static void SetUpTestCase() { + // Tests intentionally generate SIGPIPE. + TEST_PCHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR); + } + + static void TearDownTestCase() { + TEST_PCHECK(signal(SIGPIPE, SIG_DFL) != SIG_ERR); + } +}; + +TEST_F(PipeTest, Basic) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + // Ensure that the inode number is the same for each end. + struct stat rst; + ASSERT_THAT(fstat(fds[0], &rst), SyscallSucceeds()); + struct stat wst; + ASSERT_THAT(fstat(fds[1], &wst), SyscallSucceeds()); + EXPECT_EQ(rst.st_ino, wst.st_ino); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(fds[1], F_GETFL), SyscallSucceedsWithValue(O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicCloExec) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe2(fds, O_CLOEXEC), SyscallSucceeds()); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(fds[1], F_GETFL), SyscallSucceeds()); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicNoBlock) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds()); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceedsWithValue(O_NONBLOCK)); + ASSERT_THAT(fcntl(fds[1], F_GETFL), + SyscallSucceedsWithValue(O_NONBLOCK | O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicBothOptions) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe2(fds, O_NONBLOCK | O_CLOEXEC), SyscallSucceeds()); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceedsWithValue(O_NONBLOCK)); + ASSERT_THAT(fcntl(fds[1], F_GETFL), + SyscallSucceedsWithValue(O_NONBLOCK | O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicBadOptions) { + int fds[2]; + ASSERT_THAT(pipe2(fds, 0xDEAD), SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(PipeTest, Seek) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + ASSERT_THAT(lseek(fds[0], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(lseek(fds[0], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + + ASSERT_THAT(lseek(fds[0], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(lseek(fds[0], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(fds[1], F_GETFL), SyscallSucceedsWithValue(O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, AbsoluteOffsetSyscallsFail) { + // Syscalls for IO at absolute offsets fail because pipes are not seekable. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + std::vector buf(4096); + struct iovec iov; + + EXPECT_THAT(pread(fds[1], buf.data(), buf.size(), 0), + SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(pwrite(fds[0], buf.data(), buf.size(), 0), + SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(preadv(fds[1], &iov, 1, 0), SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(pwritev(fds[0], &iov, 1, 0), SyscallFailsWithErrno(ESPIPE)); + + EXPECT_THAT(close(fds[0]), SyscallSucceeds()); + EXPECT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, WriterSideCloses) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int rfd = fds[0]; + int i = 123; + ScopedThread t([rfd]() { + int j; + ASSERT_THAT(read(rfd, &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + // This will return when the close() completes. + ASSERT_THAT(read(rfd, &j, sizeof(j)), SyscallSucceeds()); + // This will return straight away. + ASSERT_THAT(read(rfd, &j, sizeof(j)), SyscallSucceeds()); + }); + // Sleep a bit so the thread can block. + absl::SleepFor(absl::Seconds(1.0)); + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + // Sleep a bit so the thread can block again. + absl::SleepFor(absl::Seconds(3.0)); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + t.Join(); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); +} + +TEST_F(PipeTest, WriterSideClosesReadDataFirst) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int i = 123; + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + ASSERT_EQ(j, i); + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceeds()); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); +} + +TEST_F(PipeTest, ReaderSideCloses) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + int i = 123; + ASSERT_THAT(write(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EPIPE)); + + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, CloseTwice) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(close(fds[1]), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(close(fds[1]), SyscallFailsWithErrno(EBADF)); +} + +// Blocking write returns EPIPE when read end is closed if nothing has been +// written. +TEST_F(PipeTest, BlockWriteClosed) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int wfd = fds[1]; + + absl::Notification notify; + ScopedThread t([wfd, ¬ify]() { + std::vector buf(kPipeSize); + // Exactly fill the pipe buffer. + ASSERT_THAT(WriteFd(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + notify.Notify(); + + // Attempt to write one more byte. Blocks. + // N.B. Don't use WriteFd, we don't want a retry. + ASSERT_THAT(write(wfd, buf.data(), 1), SyscallFailsWithErrno(EPIPE)); + }); + + notify.WaitForNotification(); + absl::SleepFor(absl::Seconds(1.0)); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + + t.Join(); + + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +// Blocking write returns EPIPE when read end is closed even if something has +// been written. +// +// FIXME: Pipe writes blocking early allows S/R to interrupt the +// write(2) call before the buffer is full. Then the next call will will return +// non-zero instead of EPIPE. +TEST_F(PipeTest, BlockPartialWriteClosed_NoRandomSave) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int wfd = fds[1]; + + ScopedThread t([wfd]() { + std::vector buf(2 * kPipeSize); + // Write more than fits in the buffer. Blocks then returns partial write + // when the other end is closed. The next call returns EPIPE. + if (IsRunningOnGvisor()) { + // FIXME: Pipe writes block early on gVisor, resulting in a + // shorter than expected partial write. + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(::testing::Gt(0))); + } else { + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(kPipeSize)); + } + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallFailsWithErrno(EPIPE)); + }); + + // Leave time for write to become blocked. + absl::SleepFor(absl::Seconds(1.0)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + + t.Join(); + + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, ReadFromClosedFd_NoRandomSave) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int rfd = fds[0]; + absl::Notification notify; + ScopedThread t([rfd, ¬ify]() { + int f; + notify.Notify(); + ASSERT_THAT(read(rfd, &f, sizeof(f)), SyscallSucceedsWithValue(sizeof(f))); + ASSERT_EQ(123, f); + }); + notify.WaitForNotification(); + // Make sure that the thread gets to read(). + absl::SleepFor(absl::Seconds(5.0)); + { + // We cannot save/restore here as the read end of pipe is closed but there + // is ongoing read() above. We will not be able to restart the read() + // successfully in restore run since the read fd is closed. + const DisableSave ds; + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + int i = 123; + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + t.Join(); + } + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, FionRead) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int data[2] = {0x12345678, 0x9101112}; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + int n = -1; + EXPECT_THAT(ioctl(fds[0], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + n = -1; + EXPECT_THAT(ioctl(fds[1], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + EXPECT_THAT(write(fds[1], data, sizeof(data)), + SyscallSucceedsWithValue(sizeof(data))); + + n = -1; + EXPECT_THAT(ioctl(fds[0], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, sizeof(data)); + n = -1; + EXPECT_THAT(ioctl(fds[1], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, sizeof(data)); +} + +// Test that opening an empty anonymous pipe RDONLY via /proc/self/fd/N does not +// block waiting for a writer. +TEST_F(PipeTest, OpenViaProcSelfFD) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Close the write end of the pipe. + wfd.release(); + + // Open other side via /proc/self/fd. It should not block. + FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(absl::StrCat("/proc/self/fd/", fds[0]), O_RDONLY)); +} + +// Test that opening and reading from an anonymous pipe (with existing writes) +// RDONLY via /proc/self/fd/N returns the existing data. +TEST_F(PipeTest, OpenViaProcSelfFDWithWrites) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Write to the pipe and then close the write fd. + char data = 'x'; + ASSERT_THAT(write(fds[1], &data, 1), SyscallSucceedsWithValue(1)); + wfd.release(); + + // Open read side via /proc/self/fd, and read from it. + FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(absl::StrCat("/proc/self/fd/", fds[0]), O_RDONLY)); + char got; + ASSERT_THAT(read(proc_self_fd.get(), &got, 1), SyscallSucceedsWithValue(1)); + + // We should get what we sent. + EXPECT_EQ(got, data); +} + +TEST_F(PipeTest, LargeFile) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + int rflags; + EXPECT_THAT(rflags = fcntl(rfd.get(), F_GETFL), SyscallSucceeds()); + + // The kernel did *not* set O_LARGEFILE. + EXPECT_EQ(rflags, 0); +} + +// Test that accessing /proc//fd/ correctly decrements the refcount of +// that file descriptor. +TEST_F(PipeTest, ProcFDReleasesFile) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Stat the pipe FD, which shouldn't alter the refcount of the write end of + // the pipe. + struct stat wst; + ASSERT_THAT(lstat(absl::StrCat("/proc/self/fd/", wfd.get()).c_str(), &wst), + SyscallSucceeds()); + + // Close the write end of the pipe and ensure that read indicates EOF. + wfd.reset(); + char buf; + ASSERT_THAT(read(rfd.get(), &buf, 1), SyscallSucceedsWithValue(0)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/poll.cc b/test/syscalls/linux/poll.cc new file mode 100644 index 000000000..897fd0bec --- /dev/null +++ b/test/syscalls/linux/poll.cc @@ -0,0 +1,279 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/base_poll_test.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { +namespace { + +class PollTest : public BasePollTest { + protected: + void SetUp() override { BasePollTest::SetUp(); } + void TearDown() override { BasePollTest::TearDown(); } +}; + +TEST_F(PollTest, InvalidFds) { + // fds is invalid because it's null, but we tell ppoll the length is non-zero. + EXPECT_THAT(poll(nullptr, 1, 1), SyscallFailsWithErrno(EFAULT)); + EXPECT_THAT(poll(nullptr, -1, 1), SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(PollTest, NullFds) { + EXPECT_THAT(poll(nullptr, 0, 10), SyscallSucceeds()); +} + +TEST_F(PollTest, ZeroTimeout) { + EXPECT_THAT(poll(nullptr, 0, 0), SyscallSucceeds()); +} + +// If random S/R interrupts the poll, SIGALRM may be delivered before poll +// restarts, causing the poll to hang forever. +TEST_F(PollTest, NegativeTimeout_NoRandomSave) { + // Negative timeout mean wait forever so set a timer. + SetTimer(absl::Milliseconds(100)); + EXPECT_THAT(poll(nullptr, 0, -1), SyscallFailsWithErrno(EINTR)); + EXPECT_TRUE(TimerFired()); +} + +TEST_F(PollTest, NonBlockingEventPOLLIN) { + // Create a pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + FileDescriptor fd0(fds[0]); + FileDescriptor fd1(fds[1]); + + // Write some data to the pipe. + char s[] = "foo\n"; + ASSERT_THAT(WriteFd(fd1.get(), s, strlen(s) + 1), SyscallSucceeds()); + + // Poll on the reader fd with POLLIN event. + struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), SyscallSucceedsWithValue(1)); + + // Should trigger POLLIN event. + EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); +} + +TEST_F(PollTest, BlockingEventPOLLIN) { + // Create a pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + FileDescriptor fd0(fds[0]); + FileDescriptor fd1(fds[1]); + + // Start a blocking poll on the read fd. + absl::Notification notify; + ScopedThread t([&fd0, ¬ify]() { + notify.Notify(); + + // Poll on the reader fd with POLLIN event. + struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, -1), SyscallSucceedsWithValue(1)); + + // Should trigger POLLIN event. + EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); + }); + + notify.WaitForNotification(); + absl::SleepFor(absl::Seconds(1.0)); + + // Write some data to the pipe. + char s[] = "foo\n"; + ASSERT_THAT(WriteFd(fd1.get(), s, strlen(s) + 1), SyscallSucceeds()); +} + +TEST_F(PollTest, NonBlockingEventPOLLHUP) { + // Create a pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + FileDescriptor fd0(fds[0]); + FileDescriptor fd1(fds[1]); + + // Close the writer fd. + fd1.reset(); + + // Poll on the reader fd with POLLIN event. + struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), SyscallSucceedsWithValue(1)); + + // Should trigger POLLHUP event. + EXPECT_EQ(poll_fd.revents & POLLHUP, POLLHUP); + + // Should not trigger POLLIN event. + EXPECT_EQ(poll_fd.revents & POLLIN, 0); +} + +TEST_F(PollTest, BlockingEventPOLLHUP) { + // Create a pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + FileDescriptor fd0(fds[0]); + FileDescriptor fd1(fds[1]); + + // Start a blocking poll on the read fd. + absl::Notification notify; + ScopedThread t([&fd0, ¬ify]() { + notify.Notify(); + + // Poll on the reader fd with POLLIN event. + struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, -1), SyscallSucceedsWithValue(1)); + + // Should trigger POLLHUP event. + EXPECT_EQ(poll_fd.revents & POLLHUP, POLLHUP); + + // Should not trigger POLLIN event. + EXPECT_EQ(poll_fd.revents & POLLIN, 0); + }); + + notify.WaitForNotification(); + absl::SleepFor(absl::Seconds(1.0)); + + // Write some data and close the writer fd. + fd1.reset(); +} + +TEST_F(PollTest, NonBlockingEventPOLLERR) { + // Create a pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + FileDescriptor fd0(fds[0]); + FileDescriptor fd1(fds[1]); + + // Close the reader fd. + fd0.reset(); + + // Poll on the writer fd with POLLOUT event. + struct pollfd poll_fd = {fd1.get(), POLLOUT, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), SyscallSucceedsWithValue(1)); + + // Should trigger POLLERR event. + EXPECT_EQ(poll_fd.revents & POLLERR, POLLERR); + + // Should also trigger POLLOUT event. + EXPECT_EQ(poll_fd.revents & POLLOUT, POLLOUT); +} + +// This test will validate that if an FD is already ready on some event, whether +// it's POLLIN or POLLOUT it will not immediately return unless that's actually +// what the caller was interested in. +TEST_F(PollTest, ImmediatelyReturnOnlyOnPollEvents) { + // Create a pipe. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + FileDescriptor fd0(fds[0]); + FileDescriptor fd1(fds[1]); + + // Wait for read related event on the write side of the pipe, since a write + // is possible on fds[1] it would mean that POLLOUT would return immediately. + // We should make sure that we're not woken up with that state that we didn't + // specificially request. + constexpr int kTimeoutMs = 100; + struct pollfd poll_fd = {fd1.get(), POLLIN | POLLPRI | POLLRDHUP, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, kTimeoutMs), + SyscallSucceedsWithValue(0)); // We should timeout. + EXPECT_EQ(poll_fd.revents, 0); // Nothing should be in returned events. + + // Now let's poll on POLLOUT and we should get back 1 fd as being ready and + // it should contain POLLOUT in the revents. + poll_fd.events = POLLOUT; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, kTimeoutMs), + SyscallSucceedsWithValue(1)); // 1 fd should have an event. + EXPECT_EQ(poll_fd.revents, POLLOUT); // POLLOUT should be in revents. +} + +// This test validates that poll(2) while data is available immediately returns. +TEST_F(PollTest, PollLevelTriggered) { + int fds[2] = {}; + ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, /*protocol=*/0, fds), + SyscallSucceeds()); + + FileDescriptor fd0(fds[0]); + FileDescriptor fd1(fds[1]); + + // Write two bytes to the socket. + const char* kBuf = "aa"; + ASSERT_THAT(RetryEINTR(send)(fd0.get(), kBuf, /*len=*/2, /*flags=*/0), + SyscallSucceedsWithValue(2)); // 2 bytes should be written. + + // Poll(2) should immediately return as there is data available to read. + constexpr int kInfiniteTimeout = -1; + struct pollfd poll_fd = {fd1.get(), POLLIN, 0}; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, /*nfds=*/1, kInfiniteTimeout), + SyscallSucceedsWithValue(1)); // 1 fd should be ready to read. + EXPECT_NE(poll_fd.revents & POLLIN, 0); + + // Read a single byte. + char read_byte = 0; + ASSERT_THAT(RetryEINTR(recv)(fd1.get(), &read_byte, /*len=*/1, /*flags=*/0), + SyscallSucceedsWithValue(1)); // 1 byte should be read. + ASSERT_EQ(read_byte, 'a'); // We should have read a single 'a'. + + // Create a separate pollfd for our second poll. + struct pollfd poll_fd_after = {fd1.get(), POLLIN, 0}; + + // Poll(2) should again immediately return since we only read one byte. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd_after, /*nfds=*/1, kInfiniteTimeout), + SyscallSucceedsWithValue(1)); // 1 fd should be ready to read. + EXPECT_NE(poll_fd_after.revents & POLLIN, 0); +} + +TEST_F(PollTest, Nfds) { + // Stash value of RLIMIT_NOFILES. + struct rlimit rlim; + TEST_PCHECK(getrlimit(RLIMIT_NOFILE, &rlim) == 0); + rlim_t max_fds = rlim.rlim_cur; + + // Create the biggest possible pollfd array such that each element is valid. + // + // Each entry in the 'fds' array refers to stdout (fd=1) and polls for + // "writable" events (events=POLLOUT). This essentially guarantees that the + // poll() is a no-op and allows negative testing of the 'nfds' parameter. + std::vector fds(max_fds, {.fd = 1, .events = POLLOUT}); + + // Verify that 'nfds' up to RLIMIT_NOFILE are allowed. + EXPECT_THAT(RetryEINTR(poll)(fds.data(), 1, 1), SyscallSucceedsWithValue(1)); + EXPECT_THAT(RetryEINTR(poll)(fds.data(), max_fds / 2, 1), + SyscallSucceedsWithValue(max_fds / 2)); + EXPECT_THAT(RetryEINTR(poll)(fds.data(), max_fds, 1), + SyscallSucceedsWithValue(max_fds)); + + // If 'nfds' exceeds RLIMIT_NOFILE then it must fail with EINVAL. + EXPECT_THAT(poll(fds.data(), max_fds + 1, 1), SyscallFailsWithErrno(EINVAL)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/ppoll.cc b/test/syscalls/linux/ppoll.cc new file mode 100644 index 000000000..f8c388c00 --- /dev/null +++ b/test/syscalls/linux/ppoll.cc @@ -0,0 +1,155 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/base_poll_test.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +// Linux and glibc have a different idea of the sizeof sigset_t. When calling +// the syscall directly, use what the kernel expects. +unsigned kSigsetSize = SIGRTMAX / 8; + +// Linux ppoll(2) differs from the glibc wrapper function in that Linux updates +// the timeout with the amount of time remaining. In order to test this behavior +// we need to use the syscall directly. +int syscallPpoll(struct pollfd* fds, nfds_t nfds, struct timespec* timeout_ts, + const sigset_t* sigmask, unsigned mask_size) { + return syscall(SYS_ppoll, fds, nfds, timeout_ts, sigmask, mask_size); +} + +class PpollTest : public BasePollTest { + protected: + void SetUp() override { BasePollTest::SetUp(); } + void TearDown() override { BasePollTest::TearDown(); } +}; + +TEST_F(PpollTest, InvalidFds) { + // fds is invalid because it's null, but we tell ppoll the length is non-zero. + struct timespec timeout = {}; + sigset_t sigmask; + TEST_PCHECK(sigemptyset(&sigmask) == 0); + EXPECT_THAT(syscallPpoll(nullptr, 1, &timeout, &sigmask, kSigsetSize), + SyscallFailsWithErrno(EFAULT)); + EXPECT_THAT(syscallPpoll(nullptr, -1, &timeout, &sigmask, kSigsetSize), + SyscallFailsWithErrno(EINVAL)); +} + +// See that when fds is null, ppoll behaves like sleep. +TEST_F(PpollTest, NullFds) { + struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); + ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), + SyscallSucceeds()); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_nsec, 0); +} + +TEST_F(PpollTest, ZeroTimeout) { + struct timespec timeout = {}; + ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), + SyscallSucceeds()); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_nsec, 0); +} + +// If random S/R interrupts the ppoll, SIGALRM may be delivered before ppoll +// restarts, causing the ppoll to hang forever. +TEST_F(PpollTest, NoTimeout_NoRandomSave) { + // When there's no timeout, ppoll may never return so set a timer. + SetTimer(absl::Milliseconds(100)); + // See that we get interrupted by the timer. + ASSERT_THAT(syscallPpoll(nullptr, 0, nullptr, nullptr, 0), + SyscallFailsWithErrno(EINTR)); + EXPECT_TRUE(TimerFired()); +} + +TEST_F(PpollTest, InvalidTimeoutNegative) { + struct timespec timeout = absl::ToTimespec(absl::Nanoseconds(-1)); + EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(PpollTest, InvalidTimeoutNotNormalized) { + struct timespec timeout = {0, 1000000001}; + EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(PpollTest, InvalidMaskSize) { + struct timespec timeout = {}; + sigset_t sigmask; + TEST_PCHECK(sigemptyset(&sigmask) == 0); + EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, &sigmask, 128), + SyscallFailsWithErrno(EINVAL)); +} + +// Verify that signals blocked by the ppoll mask (that would otherwise be +// allowed) do not interrupt ppoll. +TEST_F(PpollTest, SignalMaskBlocksSignal) { + absl::Duration duration(absl::Seconds(30)); + struct timespec timeout = absl::ToTimespec(duration); + absl::Duration timer_duration(absl::Seconds(10)); + + // Call with a mask that blocks SIGALRM. See that ppoll is not interrupted + // (i.e. returns 0) and that upon completion, the timer has fired. + sigset_t mask; + ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); + TEST_PCHECK(sigaddset(&mask, SIGALRM) == 0); + SetTimer(timer_duration); + MaybeSave(); + ASSERT_FALSE(TimerFired()); + ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, &mask, kSigsetSize), + SyscallSucceeds()); + EXPECT_TRUE(TimerFired()); + EXPECT_EQ(absl::DurationFromTimespec(timeout), absl::Duration()); +} + +// Verify that signals allowed by the ppoll mask (that would otherwise be +// blocked) interrupt ppoll. +TEST_F(PpollTest, SignalMaskAllowsSignal) { + absl::Duration duration(absl::Seconds(30)); + struct timespec timeout = absl::ToTimespec(duration); + absl::Duration timer_duration(absl::Seconds(10)); + + sigset_t mask; + ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); + + // Block SIGALRM. + auto cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, SIGALRM)); + + // Call with a mask that unblocks SIGALRM. See that ppoll is interrupted. + SetTimer(timer_duration); + MaybeSave(); + ASSERT_FALSE(TimerFired()); + ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, &mask, kSigsetSize), + SyscallFailsWithErrno(EINTR)); + EXPECT_TRUE(TimerFired()); + EXPECT_GT(absl::DurationFromTimespec(timeout), absl::Duration()); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/prctl.cc b/test/syscalls/linux/prctl.cc new file mode 100644 index 000000000..44f3df6a3 --- /dev/null +++ b/test/syscalls/linux/prctl.cc @@ -0,0 +1,171 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_bool(prctl_no_new_privs_test_child, false, + "If true, exit with the return value of prctl(PR_GET_NO_NEW_PRIVS) " + "plus an offset (see test source)."); + +namespace gvisor { +namespace testing { + +namespace { + +TEST(PrctlTest, NameInitialized) { + const size_t name_length = 20; + char name[name_length] = {}; + ASSERT_THAT(prctl(PR_GET_NAME, name), SyscallSucceeds()); + ASSERT_NE(std::string(name), ""); +} + +TEST(PrctlTest, SetNameLongName) { + const size_t name_length = 20; + const std::string long_name(name_length, 'A'); + ASSERT_THAT(prctl(PR_SET_NAME, long_name.c_str()), SyscallSucceeds()); + char truncated_name[name_length] = {}; + ASSERT_THAT(prctl(PR_GET_NAME, truncated_name), SyscallSucceeds()); + const size_t truncated_length = 15; + ASSERT_EQ(long_name.substr(0, truncated_length), std::string(truncated_name)); +} + +// Offset added to exit code from test child to distinguish from other abnormal +// exits. +constexpr int kPrctlNoNewPrivsTestChildExitBase = 100; + +TEST(PrctlTest, NoNewPrivsPreservedAcrossCloneForkAndExecve) { + // Check if no_new_privs is already set. If it is, we can still test that it's + // preserved across clone/fork/execve, but we also expect it to still be set + // at the end of the test. Otherwise, call prctl(PR_SET_NO_NEW_PRIVS) so as + // not to contaminate the original thread. + int no_new_privs; + ASSERT_THAT(no_new_privs = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), + SyscallSucceeds()); + ScopedThread([] { + ASSERT_THAT(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), SyscallSucceeds()); + EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), + SyscallSucceedsWithValue(1)); + ScopedThread([] { + EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), + SyscallSucceedsWithValue(1)); + // Note that these ASSERT_*s failing will only return from this thread, + // but this is the intended behavior. + pid_t child_pid = -1; + int execve_errno = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec("/proc/self/exe", + {"/proc/self/exe", "--prctl_no_new_privs_test_child"}, {}, + nullptr, &child_pid, &execve_errno)); + + ASSERT_GT(child_pid, 0); + ASSERT_EQ(execve_errno, 0); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceeds()); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), kPrctlNoNewPrivsTestChildExitBase + 1); + + EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), + SyscallSucceedsWithValue(1)); + }); + EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), + SyscallSucceedsWithValue(1)); + }); + EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), + SyscallSucceedsWithValue(no_new_privs)); +} + +TEST(PrctlTest, PDeathSig) { + pid_t child_pid; + + // Make the new process' parent a separate thread since the parent death + // signal fires when the parent *thread* exits. + ScopedThread([&] { + child_pid = fork(); + TEST_CHECK(child_pid >= 0); + if (child_pid == 0) { + // In child process. + TEST_CHECK(prctl(PR_SET_PDEATHSIG, SIGKILL) >= 0); + int signo; + TEST_CHECK(prctl(PR_GET_PDEATHSIG, &signo) >= 0); + TEST_CHECK(signo == SIGKILL); + // Enable tracing, then raise SIGSTOP and expect our parent to suppress + // it. + TEST_CHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) >= 0); + raise(SIGSTOP); + // Sleep until killed by our parent death signal. sleep(3) is + // async-signal-safe, absl::SleepFor isn't. + while (true) { + sleep(10); + } + } + // In parent process. + + // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << "status = " << status; + + // Suppress the SIGSTOP and detach from the child. + ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); + }); + + // The child should have been killed by its parent death SIGKILL. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + << "status = " << status; +} + +// This test is to validate that calling prctl with PR_SET_MM without the +// CAP_SYS_RESOURCE returns EPERM. +TEST(PrctlTest, InvalidPrSetMM) { + if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))) { + ASSERT_NO_ERRNO(SetCapability(CAP_SYS_RESOURCE, + false)); // Drop capability to test below. + } + ASSERT_THAT(prctl(PR_SET_MM, 0, 0, 0, 0), SyscallFailsWithErrno(EPERM)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + gvisor::testing::TestInit(&argc, &argv); + + if (FLAGS_prctl_no_new_privs_test_child) { + exit(gvisor::testing::kPrctlNoNewPrivsTestChildExitBase + + prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + } + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/prctl_setuid.cc b/test/syscalls/linux/prctl_setuid.cc new file mode 100644 index 000000000..c1b561464 --- /dev/null +++ b/test/syscalls/linux/prctl_setuid.cc @@ -0,0 +1,262 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/logging.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_int32(scratch_uid, 65534, "scratch UID"); +// This flag is used to verify that after an exec PR_GET_KEEPCAPS +// returns 0, the return code will be offset by kPrGetKeepCapsExitBase. +DEFINE_bool(prctl_pr_get_keepcaps, false, + "If true the test will verify that prctl with pr_get_keepcaps" + "returns 0. The test will exit with the result of that check."); + +// These tests exist seperately from prctl because we need to start +// them as root. Setuid() has the behavior that permissions are fully +// removed if one of the UIDs were 0 before a setuid() call. This +// behavior can be changed by using PR_SET_KEEPCAPS and that is what +// is tested here. +// +// Reference setuid(2): +// The setuid() function checks the effective user ID of +// the caller and if it is the superuser, all process-related user ID's +// are set to uid. After this has occurred, it is impossible for the +// program to regain root privileges. +// +// Thus, a set-user-ID-root program wishing to temporarily drop root +// privileges, assume the identity of an unprivileged user, and then +// regain root privileges afterward cannot use setuid(). You can +// accomplish this with seteuid(2). +namespace gvisor { +namespace testing { + +// Offset added to exit code from test child to distinguish from other abnormal +// exits. +constexpr int kPrGetKeepCapsExitBase = 100; + +namespace { + +class PrctlKeepCapsSetuidTest : public ::testing::Test { + protected: + void SetUp() override { + // PR_GET_KEEPCAPS will only return 0 or 1 (on success). + ASSERT_THAT(original_keepcaps_ = prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), + SyscallSucceeds()); + ASSERT_TRUE(original_keepcaps_ == 0 || original_keepcaps_ == 1); + } + + void TearDown() override { + // Restore PR_SET_KEEPCAPS. + ASSERT_THAT(prctl(PR_SET_KEEPCAPS, original_keepcaps_, 0, 0, 0), + SyscallSucceeds()); + + // Verify that it was restored. + ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), + SyscallSucceedsWithValue(original_keepcaps_)); + } + + // The original keep caps value exposed so tests can use it if they need. + int original_keepcaps_ = 0; +}; + +// This test will verify that a bad value, eg. not 0 or 1 for +// PR_SET_KEEPCAPS will return EINVAL as required by prctl(2). +TEST_F(PrctlKeepCapsSetuidTest, PrctlBadArgsToKeepCaps) { + ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 2, 0, 0, 0), + SyscallFailsWithErrno(EINVAL)); +} + +// This test will verify that a setuid(2) without PR_SET_KEEPCAPS will cause +// all capabilities to be dropped. +TEST_F(PrctlKeepCapsSetuidTest, SetUidNoKeepCaps) { + // getuid(2) never fails. + if (getuid() != 0) { + SKIP_IF(!IsRunningOnGvisor()); + FAIL() << "User is not root on gvisor platform."; + } + + // Do setuid in a separate thread so that after finishing this test, the + // process can still open files the test harness created before starting + // this test. Otherwise, the files are created by root (UID before the + // test), but cannot be opened by the `uid` set below after the test. After + // calling setuid(non-zero-UID), there is no way to get root privileges + // back. + ScopedThread([] { + // Start by verifying we have a capability. + TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); + + // Verify that PR_GET_KEEPCAPS is disabled. + ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), + SyscallSucceedsWithValue(0)); + + // Use syscall instead of glibc setuid wrapper because we want this setuid + // call to only apply to this task. POSIX threads, however, require that + // all threads have the same UIDs, so using the setuid wrapper sets all + // threads' real UID. + EXPECT_THAT(syscall(SYS_setuid, FLAGS_scratch_uid), SyscallSucceeds()); + + // Verify that we changed uid. + EXPECT_THAT(getuid(), SyscallSucceedsWithValue(FLAGS_scratch_uid)); + + // Verify we lost the capability in the effective set, this always happens. + TEST_CHECK(!HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); + + // We should have also lost it in the permitted set by the setuid() so + // SetCapability should fail when we try to add it back to the effective set + ASSERT_FALSE(SetCapability(CAP_SYS_ADMIN, true).ok()); + }); +} + +// This test will verify that a setuid with PR_SET_KEEPCAPS will cause +// capabilities to be retained after we switch away from the root user. +TEST_F(PrctlKeepCapsSetuidTest, SetUidKeepCaps) { + // getuid(2) never fails. + if (getuid() != 0) { + SKIP_IF(!IsRunningOnGvisor()); + FAIL() << "User is not root on gvisor platform."; + } + + // Do setuid in a separate thread so that after finishing this test, the + // process can still open files the test harness created before starting + // this test. Otherwise, the files are created by root (UID before the + // test), but cannot be opened by the `uid` set below after the test. After + // calling setuid(non-zero-UID), there is no way to get root privileges + // back. + ScopedThread([] { + // Start by verifying we have a capability. + TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); + + // Set PR_SET_KEEPCAPS. + ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); + + // Verify PR_SET_KEEPCAPS was set before we proceed. + ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), + SyscallSucceedsWithValue(1)); + + // Use syscall instead of glibc setuid wrapper because we want this setuid + // call to only apply to this task. POSIX threads, however, require that + // all threads have the same UIDs, so using the setuid wrapper sets all + // threads' real UID. + EXPECT_THAT(syscall(SYS_setuid, FLAGS_scratch_uid), SyscallSucceeds()); + + // Verify that we changed uid. + EXPECT_THAT(getuid(), SyscallSucceedsWithValue(FLAGS_scratch_uid)); + + // Verify we lost the capability in the effective set, this always happens. + TEST_CHECK(!HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); + + // We lost the capability in the effective set, but it will still + // exist in the permitted set so we can elevate the capability. + ASSERT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, true)); + + // Verify we got back the capability in the effective set. + TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); + }); +} + +// This test will verify that PR_SET_KEEPCAPS is not retained +// across an execve. According to prctl(2): +// "The "keep capabilities" value will be reset to 0 on subsequent +// calls to execve(2)." +TEST_F(PrctlKeepCapsSetuidTest, NoKeepCapsAfterExec) { + ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); + + // Verify PR_SET_KEEPCAPS was set before we proceed. + ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), SyscallSucceedsWithValue(1)); + + pid_t child_pid = -1; + int execve_errno = 0; + // Do an exec and then verify that PR_GET_KEEPCAPS returns 0 + // see the body of main below. + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( + "/proc/self/exe", {"/proc/self/exe", "--prctl_pr_get_keepcaps"}, {}, + nullptr, &child_pid, &execve_errno)); + + ASSERT_GT(child_pid, 0); + ASSERT_EQ(execve_errno, 0); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + ASSERT_TRUE(WIFEXITED(status)); + // PR_SET_KEEPCAPS should have been cleared by the exec. + // Success should return gvisor::testing::kPrGetKeepCapsExitBase + 0 + ASSERT_EQ(WEXITSTATUS(status), kPrGetKeepCapsExitBase); +} + +TEST_F(PrctlKeepCapsSetuidTest, NoKeepCapsAfterNewUserNamespace) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); + + // Fork to avoid changing the user namespace of the original test process. + pid_t const child_pid = fork(); + + if (child_pid == 0) { + // Verify that the keepcaps flag is set to 0 when we change user namespaces. + TEST_PCHECK(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0); + MaybeSave(); + + TEST_PCHECK(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0) == 1); + MaybeSave(); + + TEST_PCHECK(unshare(CLONE_NEWUSER) == 0); + MaybeSave(); + + TEST_PCHECK(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0) == 0); + MaybeSave(); + + _exit(0); + } + + int status; + ASSERT_THAT(child_pid, SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status = " << status; +} + +// This test will verify that PR_SET_KEEPCAPS and PR_GET_KEEPCAPS work correctly +TEST_F(PrctlKeepCapsSetuidTest, PrGetKeepCaps) { + // Set PR_SET_KEEPCAPS to the negation of the original. + ASSERT_THAT(prctl(PR_SET_KEEPCAPS, !original_keepcaps_, 0, 0, 0), + SyscallSucceeds()); + + // Verify it was set. + ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), + SyscallSucceedsWithValue(!original_keepcaps_)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + gvisor::testing::TestInit(&argc, &argv); + + if (FLAGS_prctl_pr_get_keepcaps) { + return gvisor::testing::kPrGetKeepCapsExitBase + + prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0); + } + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/pread64.cc b/test/syscalls/linux/pread64.cc new file mode 100644 index 000000000..4e5bcfcde --- /dev/null +++ b/test/syscalls/linux/pread64.cc @@ -0,0 +1,152 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class Pread64Test : public ::testing::Test { + void SetUp() override { + name_ = NewTempAbsPath(); + ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_CREAT, 0644)); + } + + void TearDown() override { unlink(name_.c_str()); } + + public: + std::string name_; +}; + +TEST(Pread64TestNoTempFile, BadFileDescriptor) { + char buf[1024]; + EXPECT_THAT(pread64(-1, buf, 1024, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(Pread64Test, ZeroBuffer) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR)); + + char msg[] = "hello world"; + EXPECT_THAT(pwrite64(fd.get(), msg, strlen(msg), 0), + SyscallSucceedsWithValue(strlen(msg))); + + char buf[10]; + EXPECT_THAT(pread64(fd.get(), buf, 0, 0), SyscallSucceedsWithValue(0)); +} + +TEST_F(Pread64Test, BadBuffer) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR)); + + char msg[] = "hello world"; + EXPECT_THAT(pwrite64(fd.get(), msg, strlen(msg), 0), + SyscallSucceedsWithValue(strlen(msg))); + + char* bad_buffer = nullptr; + EXPECT_THAT(pread64(fd.get(), bad_buffer, 1024, 0), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(Pread64Test, WriteOnlyNotReadable) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_WRONLY)); + + char buf[1024]; + EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(Pread64Test, DirNotReadable) { + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); + + char buf[1024]; + EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EISDIR)); +} + +TEST_F(Pread64Test, BadOffset) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDONLY)); + + char buf[1024]; + EXPECT_THAT(pread64(fd.get(), buf, 1024, -1), SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(Pread64Test, OffsetNotIncremented) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR)); + + char msg[] = "hello world"; + EXPECT_THAT(write(fd.get(), msg, strlen(msg)), + SyscallSucceedsWithValue(strlen(msg))); + int offset; + EXPECT_THAT(offset = lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); + + char buf1[1024]; + EXPECT_THAT(pread64(fd.get(), buf1, 1024, 0), + SyscallSucceedsWithValue(strlen(msg))); + EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(offset)); + + char buf2[1024]; + EXPECT_THAT(pread64(fd.get(), buf2, 1024, 3), + SyscallSucceedsWithValue(strlen(msg) - 3)); + EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(offset)); +} + +TEST_F(Pread64Test, EndOfFile) { + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDONLY)); + + char buf[1024]; + EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallSucceedsWithValue(0)); +} + +TEST(Pread64TestNoTempFile, CantReadSocketPair_NoRandomSave) { + int sock_fds[2]; + EXPECT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds), SyscallSucceeds()); + + char buf[1024]; + EXPECT_THAT(pread64(sock_fds[0], buf, 1024, 0), + SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(pread64(sock_fds[1], buf, 1024, 0), + SyscallFailsWithErrno(ESPIPE)); + + EXPECT_THAT(close(sock_fds[0]), SyscallSucceeds()); + EXPECT_THAT(close(sock_fds[1]), SyscallSucceeds()); +} + +TEST(Pread64TestNoTempFile, CantReadPipe) { + char buf[1024]; + + int pipe_fds[2]; + EXPECT_THAT(pipe(pipe_fds), SyscallSucceeds()); + + EXPECT_THAT(pread64(pipe_fds[0], buf, 1024, 0), + SyscallFailsWithErrno(ESPIPE)); + + EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); + EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/preadv.cc b/test/syscalls/linux/preadv.cc new file mode 100644 index 000000000..8d3aed43c --- /dev/null +++ b/test/syscalls/linux/preadv.cc @@ -0,0 +1,94 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/memory_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { +TEST(PreadvTest, MMConcurrencyStress) { + // Fill a one-page file with zeroes (the contents don't really matter). + const auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + /* parent = */ GetAbsoluteTestTmpdir(), + /* content = */ std::string(kPageSize, 0), TempPath::kDefaultFileMode)); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); + + // Get a one-page private mapping to read to. + const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + + // Repeatedly fork in a separate thread to force the mapping to become + // copy-on-write. + std::atomic done(false); + const ScopedThread t([&] { + while (!done.load()) { + const pid_t pid = fork(); + TEST_CHECK(pid >= 0); + if (pid == 0) { + // In child. The parent was obviously multithreaded, so it's neither + // safe nor necessary to do much more than exit. + syscall(SYS_exit_group, 0); + } + int status; + ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), + SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status = " << status; + } + }); + + // Repeatedly read to the mapping. + struct iovec iov[2]; + iov[0].iov_base = m.ptr(); + iov[0].iov_len = kPageSize / 2; + iov[1].iov_base = reinterpret_cast(m.addr() + kPageSize / 2); + iov[1].iov_len = kPageSize / 2; + constexpr absl::Duration kTestDuration = absl::Seconds(5); + const absl::Time end = absl::Now() + kTestDuration; + while (absl::Now() < end) { + // Among other causes, save/restore cycles may cause interruptions resulting + // in partial reads, so we don't expect any particular return value. + EXPECT_THAT(RetryEINTR(preadv)(fd.get(), iov, 2, 0), SyscallSucceeds()); + } + + // Stop the other thread. + done.store(true); + + // The test passes if it neither deadlocks nor crashes the OS. +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/preadv2.cc b/test/syscalls/linux/preadv2.cc new file mode 100644 index 000000000..642eed624 --- /dev/null +++ b/test/syscalls/linux/preadv2.cc @@ -0,0 +1,217 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/file_base.h" +#include "test/syscalls/linux/readv_common.h" +#include "test/util/file_descriptor.h" +#include "test/util/memory_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +#ifndef SYS_preadv2 +#if defined(__x86_64__) +#define SYS_preadv2 327 +#else +#error "Unknown architecture" +#endif +#endif // SYS_preadv2 + +#ifndef RWF_HIPRI +#define RWF_HIPRI 0x1 +#endif // RWF_HIPRI + +constexpr int kBufSize = 1024; + +std::string SetContent() { + std::string content; + for (int i = 0; i < kBufSize; i++) { + content += static_cast((i % 10) + '0'); + } + return content; +} + +// This test is the base case where we call preadv (no offset, no flags). +TEST(Preadv2Test, TestBaseCall) { + if (!IsRunningOnGvisor()) { + SKIP_BEFORE_KERNEL(/*major_version=*/4, /*minor_version=*/6); + } + std::string content = SetContent(); + + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), content, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + std::vector buf(kBufSize); + struct iovec iov; + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + + EXPECT_THAT(syscall(SYS_preadv2, fd.get(), &iov, /*iov_cnt*/ 1, + /*offset=*/0, /*flags=*/0), + SyscallSucceedsWithValue(kBufSize)); + + EXPECT_EQ(content, std::string(buf.data(), buf.size())); +} + +// This test is where we call preadv with an offset and no flags. +TEST(Preadv2Test, TestValidPositiveOffset) { + if (!IsRunningOnGvisor()) { + SKIP_BEFORE_KERNEL(/*major_version=*/4, /*minor_version=*/6); + } + std::string content = SetContent(); + const std::string prefix = "0"; + + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), prefix + content, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + std::vector buf(kBufSize, '0'); + struct iovec iov; + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + + EXPECT_THAT(syscall(SYS_preadv2, fd.get(), &iov, /*iov_cnt=*/1, + /*offset=*/prefix.size(), /*flags=*/0), + SyscallSucceedsWithValue(kBufSize)); + + EXPECT_EQ(content, std::string(buf.data(), buf.size())); +} + +// This test is the base case where we call readv by using -1 as the offset. The +// read should use the file offset, so the test increments it by one prior to +// calling preadv2. +TEST(Preadv2Test, TestNegativeOneOffset) { + if (!IsRunningOnGvisor()) { + SKIP_BEFORE_KERNEL(/*major_version=*/4, /*minor_version=*/6); + } + std::string content = SetContent(); + const std::string prefix = "231"; + + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), prefix + content, TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + ASSERT_THAT(lseek(fd.get(), prefix.size(), SEEK_SET), + SyscallSucceedsWithValue(prefix.size())); + + std::vector buf(kBufSize, '0'); + struct iovec iov; + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + + EXPECT_THAT(syscall(SYS_preadv2, fd.get(), &iov, /*iov_cnt=*/1, + /*offset=*/static_cast(-1), /*flags=*/0), + SyscallSucceedsWithValue(kBufSize)); + + EXPECT_EQ(content, std::string(buf.data(), buf.size())); +} + +// This test calls preadv2 with an invalid flag. +TEST(Preadv2Test, TestInvalidFlag) { + if (!IsRunningOnGvisor()) { + SKIP_BEFORE_KERNEL(/*major_version=*/4, /*minor_version=*/6); + } + + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY | O_DIRECT)); + + struct iovec iov; + + EXPECT_THAT(syscall(SYS_preadv2, fd.get(), &iov, /*iov_cnt=*/1, + /*offset=*/0, /*flags=*/RWF_HIPRI << 1), + SyscallFailsWithErrno(EINVAL)); +} + +// This test calls preadv2 with an invalid offset. +TEST(Preadv2Test, TestInvalidOffset) { + if (!IsRunningOnGvisor()) { + SKIP_BEFORE_KERNEL(/*major_version=*/4, /*minor_version=*/6); + } + + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY | O_DIRECT)); + struct iovec iov; + + EXPECT_THAT(syscall(SYS_preadv2, fd.get(), &iov, /*iov_cnt=*/1, + /*offset=*/static_cast(-8), /*flags=*/RWF_HIPRI), + SyscallFailsWithErrno(EINVAL)); +} + +// This test calls preadv with a file set O_WRONLY. +TEST(Preadv2Test, TestUnreadableFile) { + if (!IsRunningOnGvisor()) { + SKIP_BEFORE_KERNEL(/*major_version=*/4, /*minor_version=*/6); + } + + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); + struct iovec iov; + + EXPECT_THAT(syscall(SYS_preadv2, fd.get(), &iov, /*iov_cnt=*/1, + /*offset=*/0, /*flags=*/0), + SyscallFailsWithErrno(EBADF)); +} + +// Calling preadv2 with a non-negative offset calls preadv. Calling preadv with +// an unseekable file is not allowed. A pipe is used for an unseekable file. +TEST(Preadv2Test, TestUnseekableFile) { + if (!IsRunningOnGvisor()) { + SKIP_BEFORE_KERNEL(/*major_version=*/4, /*minor_version=*/6); + } + + int pipe_fds[2]; + + ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); + + struct iovec iov; + + EXPECT_THAT(syscall(SYS_preadv2, pipe_fds[0], &iov, /*iov_cnt=*/1, + /*offset=*/2, /*flags=*/0), + SyscallFailsWithErrno(ESPIPE)); + + EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); + EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/priority.cc b/test/syscalls/linux/priority.cc new file mode 100644 index 000000000..69a58a422 --- /dev/null +++ b/test/syscalls/linux/priority.cc @@ -0,0 +1,215 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "test/util/capability_util.h" +#include "test/util/fs_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// These tests are for both the getpriority(2) and setpriority(2) syscalls +// These tests are very rudimentary because getpriority and setpriority +// have not yet been fully implemented. + +// Getpriority does something +TEST(GetpriorityTest, Implemented) { + // "getpriority() can legitimately return the value -1, it is necessary to + // clear the external variable errno prior to the call" + errno = 0; + EXPECT_THAT(getpriority(PRIO_PROCESS, /*who=*/0), SyscallSucceeds()); +} + +// Invalid which +TEST(GetpriorityTest, InvalidWhich) { + errno = 0; + EXPECT_THAT(getpriority(/*which=*/3, /*who=*/0), + SyscallFailsWithErrno(EINVAL)); +} + +// Process is found when which=PRIO_PROCESS +TEST(GetpriorityTest, ValidWho) { + errno = 0; + EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), SyscallSucceeds()); +} + +// Process is not found when which=PRIO_PROCESS +TEST(GetpriorityTest, InvalidWho) { + errno = 0; + // Flaky, but it's tough to avoid a race condition when finding an unused pid + EXPECT_THAT(getpriority(PRIO_PROCESS, /*who=*/INT_MAX - 1), + SyscallFailsWithErrno(ESRCH)); +} + +// Setpriority does something +TEST(SetpriorityTest, Implemented) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + // No need to clear errno for setpriority(): + // "The setpriority() call returns 0 if there is no error, or -1 if there is" + EXPECT_THAT(setpriority(PRIO_PROCESS, /*who=*/0, /*nice=*/16), + SyscallSucceeds()); +} + +// Invalid which +TEST(Setpriority, InvalidWhich) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + EXPECT_THAT(setpriority(/*which=*/3, /*who=*/0, /*nice=*/16), + SyscallFailsWithErrno(EINVAL)); +} + +// Process is found when which=PRIO_PROCESS +TEST(SetpriorityTest, ValidWho) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/16), + SyscallSucceeds()); +} + +// niceval is within the range [-20, 19] +TEST(SetpriorityTest, InsideRange) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + // Set 0 < niceval < 19 + int nice = 12; + EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), nice), SyscallSucceeds()); + + errno = 0; + EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), + SyscallSucceedsWithValue(nice)); + + // Set -20 < niceval < 0 + nice = -12; + EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), nice), SyscallSucceeds()); + + errno = 0; + EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), + SyscallSucceedsWithValue(nice)); +} + +// Verify that priority/niceness are exposed via /proc/PID/stat. +TEST(SetpriorityTest, NicenessExposedViaProcfs) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + constexpr int kNiceVal = 12; + ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), kNiceVal), SyscallSucceeds()); + + errno = 0; + ASSERT_THAT(getpriority(PRIO_PROCESS, getpid()), + SyscallSucceedsWithValue(kNiceVal)); + + // Now verify we can read that same value via /proc/self/stat. + std::string proc_stat; + ASSERT_NO_ERRNO(GetContents("/proc/self/stat", &proc_stat)); + std::vector pieces = absl::StrSplit(proc_stat, ' '); + ASSERT_GT(pieces.size(), 20); + + int niceness_procfs = 0; + ASSERT_TRUE(absl::SimpleAtoi(pieces[18], &niceness_procfs)); + EXPECT_EQ(niceness_procfs, kNiceVal); +} + +// In the kernel's implementation, values outside the range of [-20, 19] are +// truncated to these minimum and maximum values. See +// https://elixir.bootlin.com/linux/v4.4/source/kernel/sys.c#L190 +TEST(SetpriorityTest, OutsideRange) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + // Set niceval > 19 + EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/100), + SyscallSucceeds()); + + errno = 0; + // Test niceval truncated to 19 + EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), + SyscallSucceedsWithValue(/*maxnice=*/19)); + + // Set niceval < -20 + EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/-100), + SyscallSucceeds()); + + errno = 0; + // Test niceval truncated to -20 + EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), + SyscallSucceedsWithValue(/*minnice=*/-20)); +} + +// Process is not found when which=PRIO_PROCESS +TEST(SetpriorityTest, InvalidWho) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + // Flaky, but it's tough to avoid a race condition when finding an unused pid + EXPECT_THAT(setpriority(PRIO_PROCESS, + /*who=*/INT_MAX - 1, + /*nice=*/16), + SyscallFailsWithErrno(ESRCH)); +} + +// Nice succeeds, correctly modifies (or in this case does not +// modify priority of process +TEST(SetpriorityTest, NiceSucceeds) { + errno = 0; + const int priority_before = getpriority(PRIO_PROCESS, /*who=*/0); + ASSERT_THAT(nice(/*inc=*/0), SyscallSucceeds()); + + // nice(0) should not change priority + EXPECT_EQ(priority_before, getpriority(PRIO_PROCESS, /*who=*/0)); +} + +// Threads resulting from clone() maintain parent's priority +// Changes to child priority do not affect parent's priority +TEST(GetpriorityTest, CloneMaintainsPriority) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); + + constexpr int kParentPriority = 16; + constexpr int kChildPriority = 14; + ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), kParentPriority), + SyscallSucceeds()); + + ScopedThread([kParentPriority, kChildPriority]() { + // Check that priority equals that of parent thread + pid_t my_tid; + EXPECT_THAT(my_tid = syscall(__NR_gettid), SyscallSucceeds()); + EXPECT_THAT(getpriority(PRIO_PROCESS, my_tid), + SyscallSucceedsWithValue(kParentPriority)); + + // Change the child thread's priority + EXPECT_THAT(setpriority(PRIO_PROCESS, my_tid, kChildPriority), + SyscallSucceeds()); + }); + + // Check that parent's priority reemained the same even though + // the child's priority was altered + EXPECT_EQ(kParentPriority, getpriority(PRIO_PROCESS, syscall(__NR_gettid))); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/priority_execve.cc b/test/syscalls/linux/priority_execve.cc new file mode 100644 index 000000000..5604bd3d0 --- /dev/null +++ b/test/syscalls/linux/priority_execve.cc @@ -0,0 +1,42 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv, char** envp) { + errno = 0; + int prio = getpriority(PRIO_PROCESS, getpid()); + + // NOTE: getpriority() can legitimately return negative values + // in the range [-20, 0). If errno is set, exit with a value that + // could not be reached by a valid priority. Valid exit values + // for the test are in the range [1, 40], so we'll use 0. + if (errno != 0) { + printf("getpriority() failed with errno = %d\n", errno); + exit(0); + } + + // Used by test to verify priority is being maintained through + // calls to execve(). Since prio should always be in the range + // [-20, 19], we offset by 20 so as not to have negative exit codes. + exit(20 - prio); + + return 0; +} diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc new file mode 100644 index 000000000..e64df97b0 --- /dev/null +++ b/test/syscalls/linux/proc.cc @@ -0,0 +1,1830 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/ascii.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/capability_util.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +// NOTE: No, this isn't really a syscall but this is a really simple +// way to get it tested on both gVisor, PTrace and Linux. + +using ::testing::AllOf; +using ::testing::ContainerEq; +using ::testing::Contains; +using ::testing::ContainsRegex; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::IsSupersetOf; +using ::testing::Pair; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; + +// Exported by glibc. +extern char** environ; + +namespace gvisor { +namespace testing { +namespace { + +// O_LARGEFILE as defined by Linux. glibc tries to be clever by setting it to 0 +// because "it isn't needed", even though Linux can return it via F_GETFL. +constexpr int kOLargeFile = 00100000; + +// Takes the subprocess command line and pid. +// If it returns !OK, WithSubprocess returns immediately. +using SubprocessCallback = std::function; + +std::vector saved_argv; // NOLINT + +// Helper function to dump /proc/{pid}/status and check the +// state data. State should = "Z" for zombied or "RSD" for +// running, interruptible sleeping (S), or uninterruptible sleep +// (D). +void CompareProcessState(absl::string_view state, int pid) { + auto status_file = ASSERT_NO_ERRNO_AND_VALUE( + GetContents(absl::StrCat("/proc/", pid, "/status"))); + EXPECT_THAT(status_file, ContainsRegex(absl::StrCat("State:.[", state, + "]\\s+\\(\\w+\\)"))); +} + +// Run callbacks while a subprocess is running, zombied, and/or exited. +PosixError WithSubprocess(SubprocessCallback const& running, + SubprocessCallback const& zombied, + SubprocessCallback const& exited) { + int pipe_fds[2] = {}; + if (pipe(pipe_fds) < 0) { + return PosixError(errno, "pipe"); + } + + int child_pid = fork(); + if (child_pid < 0) { + return PosixError(errno, "fork"); + } + + if (child_pid == 0) { + close(pipe_fds[0]); // Close the read end. + const DisableSave ds; // Timing issues. + + // Write to the pipe to tell it we're ready. + char buf = 'a'; + int res = 0; + res = WriteFd(pipe_fds[1], &buf, sizeof(buf)); + TEST_CHECK_MSG(res == sizeof(buf), "Write failure in subprocess"); + + while (true) { + SleepSafe(absl::Milliseconds(100)); + } + __builtin_unreachable(); + } + + close(pipe_fds[1]); // Close the write end. + + int status = 0; + auto wait_cleanup = Cleanup([child_pid, &status] { + EXPECT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); + }); + auto kill_cleanup = Cleanup([child_pid] { + EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); + }); + + // Wait for the child. + char buf = 0; + int res = ReadFd(pipe_fds[0], &buf, sizeof(buf)); + if (res < 0) { + return PosixError(errno, "Read from pipe"); + } else if (res == 0) { + return PosixError(EPIPE, "Unable to read from pipe: EOF"); + } + + if (running) { + // The first arg, RSD, refers to a "running process", or a process with a + // state of Running (R), Interruptable Sleep (S) or Uninterruptable + // Sleep (D). + CompareProcessState("RSD", child_pid); + RETURN_IF_ERRNO(running(child_pid)); + } + + // Kill the process. + kill_cleanup.Release()(); + siginfo_t info; + // Wait until the child process has exited (WEXITED flag) but don't + // reap the child (WNOWAIT flag). + waitid(P_PID, child_pid, &info, WNOWAIT | WEXITED); + + if (zombied) { + // Arg of "Z" refers to a Zombied Process. + CompareProcessState("Z", child_pid); + RETURN_IF_ERRNO(zombied(child_pid)); + } + + // Wait on the process. + wait_cleanup.Release()(); + // If the process is reaped, then then this should return + // with ECHILD. + EXPECT_THAT(waitpid(child_pid, &status, WNOHANG), + SyscallFailsWithErrno(ECHILD)); + + if (exited) { + RETURN_IF_ERRNO(exited(child_pid)); + } + + return NoError(); +} + +// Access the file returned by name when a subprocess is running. +PosixError AccessWhileRunning(std::function name, int flags, + std::function access) { + FileDescriptor fd; + return WithSubprocess( + [&](int pid) -> PosixError { + // Running. + ASSIGN_OR_RETURN_ERRNO(fd, Open(name(pid), flags)); + + access(fd.get()); + return NoError(); + }, + nullptr, nullptr); +} + +// Access the file returned by name when the a subprocess is zombied. +PosixError AccessWhileZombied(std::function name, int flags, + std::function access) { + FileDescriptor fd; + return WithSubprocess( + [&](int pid) -> PosixError { + // Running. + ASSIGN_OR_RETURN_ERRNO(fd, Open(name(pid), flags)); + return NoError(); + }, + [&](int pid) -> PosixError { + // Zombied. + access(fd.get()); + return NoError(); + }, + nullptr); +} + +// Access the file returned by name when the a subprocess is exited. +PosixError AccessWhileExited(std::function name, int flags, + std::function access) { + FileDescriptor fd; + return WithSubprocess( + [&](int pid) -> PosixError { + // Running. + ASSIGN_OR_RETURN_ERRNO(fd, Open(name(pid), flags)); + return NoError(); + }, + nullptr, + [&](int pid) -> PosixError { + // Exited. + access(fd.get()); + return NoError(); + }); +} + +// ReadFd(fd=/proc/PID/basename) while PID is running. +int ReadWhileRunning(std::string const& basename, void* buf, size_t count) { + int ret = 0; + int err = 0; + EXPECT_NO_ERRNO(AccessWhileRunning( + [&](int pid) -> std::string { + return absl::StrCat("/proc/", pid, "/", basename); + }, + O_RDONLY, + [&](int fd) { + ret = ReadFd(fd, buf, count); + err = errno; + })); + errno = err; + return ret; +} + +// ReadFd(fd=/proc/PID/basename) while PID is zombied. +int ReadWhileZombied(std::string const& basename, void* buf, size_t count) { + int ret = 0; + int err = 0; + EXPECT_NO_ERRNO(AccessWhileZombied( + [&](int pid) -> std::string { + return absl::StrCat("/proc/", pid, "/", basename); + }, + O_RDONLY, + [&](int fd) { + ret = ReadFd(fd, buf, count); + err = errno; + })); + errno = err; + return ret; +} + +// ReadFd(fd=/proc/PID/basename) while PID is exited. +int ReadWhileExited(std::string const& basename, void* buf, size_t count) { + int ret = 0; + int err = 0; + EXPECT_NO_ERRNO(AccessWhileExited( + [&](int pid) -> std::string { + return absl::StrCat("/proc/", pid, "/", basename); + }, + O_RDONLY, + [&](int fd) { + ret = ReadFd(fd, buf, count); + err = errno; + })); + errno = err; + return ret; +} + +// readlinkat(fd=/proc/PID/, basename) while PID is running. +int ReadlinkWhileRunning(std::string const& basename, char* buf, size_t count) { + int ret = 0; + int err = 0; + EXPECT_NO_ERRNO(AccessWhileRunning( + [&](int pid) -> std::string { return absl::StrCat("/proc/", pid, "/"); }, + O_DIRECTORY, + [&](int fd) { + ret = readlinkat(fd, basename.c_str(), buf, count); + err = errno; + })); + errno = err; + return ret; +} + +// readlinkat(fd=/proc/PID/, basename) while PID is zombied. +int ReadlinkWhileZombied(std::string const& basename, char* buf, size_t count) { + int ret = 0; + int err = 0; + EXPECT_NO_ERRNO(AccessWhileZombied( + [&](int pid) -> std::string { return absl::StrCat("/proc/", pid, "/"); }, + O_DIRECTORY, + [&](int fd) { + ret = readlinkat(fd, basename.c_str(), buf, count); + err = errno; + })); + errno = err; + return ret; +} + +// readlinkat(fd=/proc/PID/, basename) while PID is exited. +int ReadlinkWhileExited(std::string const& basename, char* buf, size_t count) { + int ret = 0; + int err = 0; + EXPECT_NO_ERRNO(AccessWhileExited( + [&](int pid) -> std::string { return absl::StrCat("/proc/", pid, "/"); }, + O_DIRECTORY, + [&](int fd) { + ret = readlinkat(fd, basename.c_str(), buf, count); + err = errno; + })); + errno = err; + return ret; +} + +TEST(ProcSelfTest, IsThreadGroupLeader) { + ScopedThread([] { + const pid_t tgid = getpid(); + const pid_t tid = syscall(SYS_gettid); + EXPECT_NE(tgid, tid); + auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self")); + EXPECT_EQ(link, absl::StrCat(tgid)); + }); +} + +TEST(ProcThreadSelfTest, Basic) { + const pid_t tgid = getpid(); + const pid_t tid = syscall(SYS_gettid); + EXPECT_EQ(tgid, tid); + auto link_threadself = + ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self")); + EXPECT_EQ(link_threadself, absl::StrCat(tgid, "/task/", tid)); + // Just read one file inside thread-self to ensure that the link is valid. + auto link_threadself_exe = + ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self/exe")); + auto link_procself_exe = + ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe")); + EXPECT_EQ(link_threadself_exe, link_procself_exe); +} + +TEST(ProcThreadSelfTest, Thread) { + ScopedThread([] { + const pid_t tgid = getpid(); + const pid_t tid = syscall(SYS_gettid); + EXPECT_NE(tgid, tid); + auto link_threadself = + ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self")); + + EXPECT_EQ(link_threadself, absl::StrCat(tgid, "/task/", tid)); + // Just read one file inside thread-self to ensure that the link is valid. + auto link_threadself_exe = + ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self/exe")); + auto link_procself_exe = + ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe")); + EXPECT_EQ(link_threadself_exe, link_procself_exe); + // A thread should not have "/proc//task". + struct stat s; + EXPECT_THAT(stat("/proc/thread-self/task", &s), + SyscallFailsWithErrno(ENOENT)); + }); +} + +// Returns the /proc/PID/maps entry for the MAP_PRIVATE | MAP_ANONYMOUS mapping +// m with start address addr and length len. +std::string AnonymousMapsEntry(uintptr_t addr, size_t len, int prot) { + return absl::StrCat(absl::Hex(addr, absl::PadSpec::kZeroPad8), "-", + absl::Hex(addr + len, absl::PadSpec::kZeroPad8), " ", + prot & PROT_READ ? "r" : "-", + prot & PROT_WRITE ? "w" : "-", + prot & PROT_EXEC ? "x" : "-", "p 00000000 00:00 0 "); +} + +std::string AnonymousMapsEntryForMapping(const Mapping& m, int prot) { + return AnonymousMapsEntry(m.addr(), m.len(), prot); +} + +PosixErrorOr> ReadProcSelfAuxv() { + std::string auxv_file; + RETURN_IF_ERRNO(GetContents("/proc/self/auxv", &auxv_file)); + const Elf64_auxv_t* auxv_data = + reinterpret_cast(auxv_file.data()); + std::map auxv_entries; + for (int i = 0; auxv_data[i].a_type != AT_NULL; i++) { + auto a_type = auxv_data[i].a_type; + EXPECT_EQ(0, auxv_entries.count(a_type)) << "a_type: " << a_type; + auxv_entries.emplace(a_type, auxv_data[i].a_un.a_val); + } + return auxv_entries; +} + +TEST(ProcSelfAuxv, EntryPresence) { + auto auxv_entries = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfAuxv()); + + EXPECT_EQ(auxv_entries.count(AT_ENTRY), 1); + EXPECT_EQ(auxv_entries.count(AT_PHDR), 1); + EXPECT_EQ(auxv_entries.count(AT_PHENT), 1); + EXPECT_EQ(auxv_entries.count(AT_PHNUM), 1); + EXPECT_EQ(auxv_entries.count(AT_BASE), 1); + EXPECT_EQ(auxv_entries.count(AT_CLKTCK), 1); + EXPECT_EQ(auxv_entries.count(AT_RANDOM), 1); + EXPECT_EQ(auxv_entries.count(AT_EXECFN), 1); + EXPECT_EQ(auxv_entries.count(AT_PAGESZ), 1); + EXPECT_EQ(auxv_entries.count(AT_SYSINFO_EHDR), 1); +} + +TEST(ProcSelfAuxv, EntryValues) { + auto proc_auxv = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfAuxv()); + + // We need to find the ELF auxiliary vector. The section of memory pointed to + // by envp contains some pointers to non-null pointers, followed by a single + // pointer to a null pointer, followed by the auxiliary vector. + char** envpi = environ; + while (*envpi) { + ++envpi; + } + + const Elf64_auxv_t* envp_auxv = + reinterpret_cast(envpi + 1); + int i; + for (i = 0; envp_auxv[i].a_type != AT_NULL; i++) { + auto a_type = envp_auxv[i].a_type; + EXPECT_EQ(proc_auxv.count(a_type), 1); + EXPECT_EQ(proc_auxv[a_type], envp_auxv[i].a_un.a_val) + << "a_type: " << a_type; + } + EXPECT_EQ(i, proc_auxv.size()); +} + +// Just open and read /proc/self/maps, check that we can find [stack] +TEST(ProcSelfMaps, Basic) { + auto proc_self_maps = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + + std::vector strings = absl::StrSplit(proc_self_maps, '\n'); + std::vector stacks; + // Make sure there's a stack in there. + for (const auto& str : strings) { + if (str.find("[stack]") != std::string::npos) { + stacks.push_back(str); + } + } + ASSERT_EQ(1, stacks.size()) << "[stack] not found in: " << proc_self_maps; + // Linux pads to 73 characters then we add 7. + EXPECT_EQ(80, stacks[0].length()); +} + +TEST(ProcSelfMaps, Map1) { + Mapping mapping = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_READ, MAP_PRIVATE)); + auto proc_self_maps = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + std::vector strings = absl::StrSplit(proc_self_maps, '\n'); + std::vector addrs; + // Make sure if is listed. + for (const auto& str : strings) { + if (str == AnonymousMapsEntryForMapping(mapping, PROT_READ)) { + addrs.push_back(str); + } + } + ASSERT_EQ(1, addrs.size()); +} + +TEST(ProcSelfMaps, Map2) { + // NOTE: The permissions must be different or the pages will get merged. + Mapping map1 = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_EXEC, MAP_PRIVATE)); + Mapping map2 = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_WRITE, MAP_PRIVATE)); + + auto proc_self_maps = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + std::vector strings = absl::StrSplit(proc_self_maps, '\n'); + std::vector addrs; + // Make sure if is listed. + for (const auto& str : strings) { + if (str == AnonymousMapsEntryForMapping(map1, PROT_READ | PROT_EXEC)) { + addrs.push_back(str); + } + } + ASSERT_EQ(1, addrs.size()); + addrs.clear(); + for (const auto& str : strings) { + if (str == AnonymousMapsEntryForMapping(map2, PROT_WRITE)) { + addrs.push_back(str); + } + } + ASSERT_EQ(1, addrs.size()); +} + +TEST(ProcSelfMaps, MapUnmap) { + Mapping map1 = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_EXEC, MAP_PRIVATE)); + Mapping map2 = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_WRITE, MAP_PRIVATE)); + + auto proc_self_maps = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + std::vector strings = absl::StrSplit(proc_self_maps, '\n'); + std::vector addrs; + // Make sure if is listed. + for (const auto& str : strings) { + if (str == AnonymousMapsEntryForMapping(map1, PROT_READ | PROT_EXEC)) { + addrs.push_back(str); + } + } + ASSERT_EQ(1, addrs.size()) << proc_self_maps; + addrs.clear(); + for (const auto& str : strings) { + if (str == AnonymousMapsEntryForMapping(map2, PROT_WRITE)) { + addrs.push_back(str); + } + } + ASSERT_EQ(1, addrs.size()); + + map2.reset(); + + // Read it again. + proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + strings = absl::StrSplit(proc_self_maps, '\n'); + // First entry should be there. + addrs.clear(); + for (const auto& str : strings) { + if (str == AnonymousMapsEntryForMapping(map1, PROT_READ | PROT_EXEC)) { + addrs.push_back(str); + } + } + ASSERT_EQ(1, addrs.size()); + addrs.clear(); + // But not the second. + for (const auto& str : strings) { + if (str == AnonymousMapsEntryForMapping(map2, PROT_WRITE)) { + addrs.push_back(str); + } + } + ASSERT_EQ(0, addrs.size()); +} + +TEST(ProcSelfMaps, Mprotect) { + if (!IsRunningOnGvisor()) { + // FIXME: Linux's mprotect() sometimes fails to merge VMAs in this + // case. + LOG(WARNING) << "Skipping test on Linux"; + return; + } + + // Reserve 5 pages of address space. + Mapping m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(5 * kPageSize, PROT_NONE, MAP_PRIVATE)); + + // Change the permissions on the middle 3 pages. (The first and last pages may + // be merged with other vmas on either side, so they aren't tested directly; + // they just ensure that the middle 3 pages are bracketed by VMAs with + // incompatible permissions.) + ASSERT_THAT(mprotect(reinterpret_cast(m.addr() + kPageSize), + 3 * kPageSize, PROT_READ), + SyscallSucceeds()); + + // Check that the middle 3 pages make up a single VMA. + auto proc_self_maps = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + std::vector strings = absl::StrSplit(proc_self_maps, '\n'); + EXPECT_THAT(strings, Contains(AnonymousMapsEntry(m.addr() + kPageSize, + 3 * kPageSize, PROT_READ))); + + // Change the permissions on the middle page only. + ASSERT_THAT(mprotect(reinterpret_cast(m.addr() + 2 * kPageSize), + kPageSize, PROT_READ | PROT_WRITE), + SyscallSucceeds()); + + // Check that the single VMA has been split into 3 VMAs. + proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + strings = absl::StrSplit(proc_self_maps, '\n'); + EXPECT_THAT( + strings, + IsSupersetOf( + {AnonymousMapsEntry(m.addr() + kPageSize, kPageSize, PROT_READ), + AnonymousMapsEntry(m.addr() + 2 * kPageSize, kPageSize, + PROT_READ | PROT_WRITE), + AnonymousMapsEntry(m.addr() + 3 * kPageSize, kPageSize, + PROT_READ)})); + + // Change the permissions on the middle page back. + ASSERT_THAT(mprotect(reinterpret_cast(m.addr() + 2 * kPageSize), + kPageSize, PROT_READ), + SyscallSucceeds()); + + // Check that the 3 VMAs have been merged back into a single VMA. + proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + strings = absl::StrSplit(proc_self_maps, '\n'); + EXPECT_THAT(strings, Contains(AnonymousMapsEntry(m.addr() + kPageSize, + 3 * kPageSize, PROT_READ))); +} + +TEST(ProcSelfFd, OpenFd) { + int pipe_fds[2]; + ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds()); + + // Reopen the write end. + const std::string path = absl::StrCat("/proc/self/fd/", pipe_fds[1]); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_WRONLY)); + + // Ensure that a read/write works. + const std::string data = "hello"; + std::unique_ptr buffer(new char[data.size()]); + EXPECT_THAT(write(fd.get(), data.c_str(), data.size()), + SyscallSucceedsWithValue(5)); + EXPECT_THAT(read(pipe_fds[0], buffer.get(), data.size()), + SyscallSucceedsWithValue(5)); + EXPECT_EQ(strncmp(buffer.get(), data.c_str(), data.size()), 0); + + // Cleanup. + ASSERT_THAT(close(pipe_fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(pipe_fds[1]), SyscallSucceeds()); +} + +TEST(ProcSelfFdInfo, CorrectFds) { + // Make sure there is at least one open file. + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); + + // Get files in /proc/self/fd. + auto fd_files = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/fd", false)); + + // Get files in /proc/self/fdinfo. + auto fdinfo_files = + ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/fdinfo", false)); + + // They should contain the same fds. + EXPECT_THAT(fd_files, UnorderedElementsAreArray(fdinfo_files)); + + // Both should contain fd. + auto fd_s = absl::StrCat(fd.get()); + EXPECT_THAT(fd_files, Contains(fd_s)); +} + +TEST(ProcSelfFdInfo, Flags) { + std::string path = NewTempAbsPath(); + + // Create file here with O_CREAT to test that O_CREAT does not appear in + // fdinfo flags. + int flags = O_CREAT | O_RDWR | O_APPEND | O_CLOEXEC; + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, flags, 0644)); + + // Automatically delete path. + TempPath temp_path(path); + + // O_CREAT does not appear in fdinfo flags. + flags &= ~O_CREAT; + + // O_LARGEFILE always appears (on x86_64). + flags |= kOLargeFile; + + auto fd_info = ASSERT_NO_ERRNO_AND_VALUE( + GetContents(absl::StrCat("/proc/self/fdinfo/", fd.get()))); + EXPECT_THAT(fd_info, HasSubstr(absl::StrFormat("flags:\t%#o", flags))); +} + +TEST(ProcSelfExe, Absolute) { + auto exe = ASSERT_NO_ERRNO_AND_VALUE( + ReadLink(absl::StrCat("/proc/", getpid(), "/exe"))); + EXPECT_EQ(exe[0], '/'); +} + +// Sanity check for /proc/cpuinfo fields that must be present. +TEST(ProcCpuinfo, RequiredFieldsArePresent) { + std::string proc_cpuinfo = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/cpuinfo")); + ASSERT_FALSE(proc_cpuinfo.empty()); + std::vector cpuinfo_fields = absl::StrSplit(proc_cpuinfo, '\n'); + + // This list of "required" fields is taken from reading the file + // arch/x86/kernel/cpu/proc.c and seeing which fields will be unconditionally + // printed by the kernel. + static const char* required_fields[] = { + "processor", + "vendor_id", + "cpu family", + "model\t\t:", + "model name", + "stepping", + "cpu MHz", + "fpu\t\t:", + "fpu_exception", + "cpuid level", + "wp", + "bogomips", + "clflush size", + "cache_alignment", + "address sizes", + "power management", + }; + + // Check that the usual fields are there. We don't really care about the + // contents. + for (const std::string& field : required_fields) { + EXPECT_THAT(proc_cpuinfo, HasSubstr(field)); + } +} + +// Sanity checks that uptime is present. +TEST(ProcUptime, IsPresent) { + std::string proc_uptime = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/uptime")); + ASSERT_FALSE(proc_uptime.empty()); + std::vector uptime_parts = absl::StrSplit(proc_uptime, ' '); + + // Parse once. + double uptime0, uptime1, idletime0, idletime1; + ASSERT_TRUE(absl::SimpleAtod(uptime_parts[0], &uptime0)); + ASSERT_TRUE(absl::SimpleAtod(uptime_parts[1], &idletime0)); + + // Sleep for one second. + absl::SleepFor(absl::Seconds(1)); + + // Parse again. + proc_uptime = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/uptime")); + ASSERT_FALSE(proc_uptime.empty()); + uptime_parts = absl::StrSplit(proc_uptime, ' '); + ASSERT_TRUE(absl::SimpleAtod(uptime_parts[0], &uptime1)); + ASSERT_TRUE(absl::SimpleAtod(uptime_parts[1], &idletime1)); + + // Sanity check. + // + // We assert that between 0.99 and 59.99 seconds have passed. If more than a + // minute has passed, then we must be executing really, really slowly. + EXPECT_GE(uptime0, 0.0); + EXPECT_GE(idletime0, 0.0); + EXPECT_GT(uptime1, uptime0); + EXPECT_GE(uptime1, uptime0 + 0.99); + EXPECT_LE(uptime1, uptime0 + 59.99); + EXPECT_GE(idletime1, idletime0); +} + +TEST(ProcMeminfo, ContainsBasicFields) { + std::string proc_meminfo = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/meminfo")); + EXPECT_THAT(proc_meminfo, AllOf(ContainsRegex(R"(MemTotal:\s+[0-9]+ kB)"), + ContainsRegex(R"(MemFree:\s+[0-9]+ kB)"))); +} + +TEST(ProcStat, ContainsBasicFields) { + std::string proc_stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat")); + + std::vector names; + for (auto const& line : absl::StrSplit(proc_stat, '\n')) { + std::vector fields = + absl::StrSplit(line, ' ', absl::SkipWhitespace()); + if (fields.empty()) { + continue; + } + names.push_back(fields[0]); + } + + EXPECT_THAT(names, + IsSupersetOf({"cpu", "intr", "ctxt", "btime", "processes", + "procs_running", "procs_blocked", "softirq"})); +} + +TEST(ProcStat, EndsWithNewline) { + std::string proc_stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat")); + EXPECT_EQ(proc_stat.back(), '\n'); +} + +TEST(ProcStat, Fields) { + std::string proc_stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat")); + + std::vector names; + for (auto const& line : absl::StrSplit(proc_stat, '\n')) { + std::vector fields = + absl::StrSplit(line, ' ', absl::SkipWhitespace()); + if (fields.empty()) { + continue; + } + + if (absl::StartsWith(fields[0], "cpu")) { + // As of Linux 3.11, each CPU entry has 10 fields, plus the name. + EXPECT_GE(fields.size(), 11) << proc_stat; + } else if (fields[0] == "ctxt") { + // Single field. + EXPECT_EQ(fields.size(), 2) << proc_stat; + } else if (fields[0] == "btime") { + // Single field. + EXPECT_EQ(fields.size(), 2) << proc_stat; + } else if (fields[0] == "itime") { + // Single field. + ASSERT_EQ(fields.size(), 2) << proc_stat; + // This is the only floating point field. + double val; + EXPECT_TRUE(absl::SimpleAtod(fields[1], &val)) << proc_stat; + continue; + } else if (fields[0] == "processes") { + // Single field. + EXPECT_EQ(fields.size(), 2) << proc_stat; + } else if (fields[0] == "procs_running") { + // Single field. + EXPECT_EQ(fields.size(), 2) << proc_stat; + } else if (fields[0] == "procs_blocked") { + // Single field. + EXPECT_EQ(fields.size(), 2) << proc_stat; + } else if (fields[0] == "softirq") { + // As of Linux 3.11, there are 10 softirqs. 12 fields for name + total. + EXPECT_GE(fields.size(), 12) << proc_stat; + } + + // All fields besides itime are valid base 10 numbers. + for (size_t i = 1; i < fields.size(); i++) { + uint64_t val; + EXPECT_TRUE(absl::SimpleAtoi(fields[i], &val)) << proc_stat; + } + } +} + +TEST(ProcLoadavg, EndsWithNewline) { + std::string proc_loadvg = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/loadavg")); + EXPECT_EQ(proc_loadvg.back(), '\n'); +} + +TEST(ProcLoadavg, Fields) { + std::string proc_loadvg = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/loadavg")); + std::vector lines = absl::StrSplit(proc_loadvg, '\n'); + + // Single line. + EXPECT_EQ(lines.size(), 2) << proc_loadvg; + + std::vector fields = + absl::StrSplit(lines[0], absl::ByAnyChar(" /"), absl::SkipWhitespace()); + + // Six fields. + EXPECT_EQ(fields.size(), 6) << proc_loadvg; + + double val; + uint64_t val2; + // First three fields are floating point numbers. + EXPECT_TRUE(absl::SimpleAtod(fields[0], &val)) << proc_loadvg; + EXPECT_TRUE(absl::SimpleAtod(fields[1], &val)) << proc_loadvg; + EXPECT_TRUE(absl::SimpleAtod(fields[2], &val)) << proc_loadvg; + // Rest of the fields are valid base 10 numbers. + EXPECT_TRUE(absl::SimpleAtoi(fields[3], &val2)) << proc_loadvg; + EXPECT_TRUE(absl::SimpleAtoi(fields[4], &val2)) << proc_loadvg; + EXPECT_TRUE(absl::SimpleAtoi(fields[5], &val2)) << proc_loadvg; +} + +// NOTE: Tests in priority.cc also check certain priority related fields in +// /proc/self/stat. + +class ProcPidStatTest : public ::testing::TestWithParam {}; + +TEST_P(ProcPidStatTest, HasBasicFields) { + std::string proc_pid_stat = ASSERT_NO_ERRNO_AND_VALUE( + GetContents(absl::StrCat("/proc/", GetParam(), "/stat"))); + + ASSERT_FALSE(proc_pid_stat.empty()); + std::vector fields = absl::StrSplit(proc_pid_stat, ' '); + ASSERT_GE(fields.size(), 24); + EXPECT_EQ(absl::StrCat(getpid()), fields[0]); + // fields[1] is the thread name. + EXPECT_EQ("R", fields[2]); // task state + EXPECT_EQ(absl::StrCat(getppid()), fields[3]); + + uint64_t vss; + ASSERT_TRUE(absl::SimpleAtoi(fields[22], &vss)); + EXPECT_GT(vss, 0); + + uint64_t rss; + ASSERT_TRUE(absl::SimpleAtoi(fields[23], &rss)); + EXPECT_GT(rss, 0); +} + +INSTANTIATE_TEST_CASE_P(SelfAndNumericPid, ProcPidStatTest, + ::testing::Values("self", absl::StrCat(getpid()))); + +using ProcPidStatmTest = ::testing::TestWithParam; + +TEST_P(ProcPidStatmTest, HasBasicFields) { + std::string proc_pid_statm = ASSERT_NO_ERRNO_AND_VALUE( + GetContents(absl::StrCat("/proc/", GetParam(), "/statm"))); + ASSERT_FALSE(proc_pid_statm.empty()); + std::vector fields = absl::StrSplit(proc_pid_statm, ' '); + ASSERT_GE(fields.size(), 7); + + uint64_t vss; + ASSERT_TRUE(absl::SimpleAtoi(fields[0], &vss)); + EXPECT_GT(vss, 0); + + uint64_t rss; + ASSERT_TRUE(absl::SimpleAtoi(fields[1], &rss)); + EXPECT_GT(rss, 0); +} + +INSTANTIATE_TEST_CASE_P(SelfAndNumericPid, ProcPidStatmTest, + ::testing::Values("self", absl::StrCat(getpid()))); + +PosixErrorOr CurrentRSS() { + ASSIGN_OR_RETURN_ERRNO(auto proc_self_stat, GetContents("/proc/self/stat")); + if (proc_self_stat.empty()) { + return PosixError(EINVAL, "empty /proc/self/stat"); + } + + std::vector fields = absl::StrSplit(proc_self_stat, ' '); + if (fields.size() < 24) { + return PosixError( + EINVAL, + absl::StrCat("/proc/self/stat has too few fields: ", proc_self_stat)); + } + + uint64_t rss; + if (!absl::SimpleAtoi(fields[23], &rss)) { + return PosixError( + EINVAL, absl::StrCat("/proc/self/stat RSS field is not a number: ", + fields[23])); + } + + // RSS is given in number of pages. + return rss * kPageSize; +} + +// The size of mapping created by MapPopulateRSS. +constexpr uint64_t kMappingSize = 100 << 20; + +// Tolerance on RSS comparisons to account for background thread mappings, +// reclaimed pages, newly faulted pages, etc. +constexpr uint64_t kRSSTolerance = 5 << 20; + +// Capture RSS before and after an anonymous mapping with passed prot. +void MapPopulateRSS(int prot, uint64_t* before, uint64_t* after) { + *before = ASSERT_NO_ERRNO_AND_VALUE(CurrentRSS()); + + // N.B. The kernel asynchronously accumulates per-task RSS counters into the + // mm RSS, which is exposed by /proc/PID/stat. Task exit is a synchronization + // point (kernel/exit.c:do_exit -> sync_mm_rss), so perform the mapping on + // another thread to ensure it is reflected in RSS after the thread exits. + Mapping mapping; + ScopedThread t([&mapping, prot] { + mapping = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kMappingSize, prot, MAP_PRIVATE | MAP_POPULATE)); + }); + t.Join(); + + *after = ASSERT_NO_ERRNO_AND_VALUE(CurrentRSS()); +} + +// TODO: Test for PROT_READ + MAP_POPULATE anonymous mappings. Their +// semantics are more subtle: +// +// Small pages -> Zero page mapped, not counted in RSS +// (mm/memory.c:do_anonymous_page). +// +// Huge pages (THP enabled, use_zero_page=0) -> Pages committed +// (mm/memory.c:__handle_mm_fault -> create_huge_pmd). +// +// Huge pages (THP enabled, use_zero_page=1) -> Zero page mapped, not counted in +// RSS (mm/huge_memory.c:do_huge_pmd_anonymous_page). + +// PROT_WRITE + MAP_POPULATE anonymous mappings are always committed. +TEST(ProcSelfStat, PopulateWriteRSS) { + uint64_t before, after; + MapPopulateRSS(PROT_READ | PROT_WRITE, &before, &after); + + // Mapping is committed. + EXPECT_NEAR(before + kMappingSize, after, kRSSTolerance); +} + +// PROT_NONE + MAP_POPULATE anonymous mappings are never committed. +TEST(ProcSelfStat, PopulateNoneRSS) { + uint64_t before, after; + MapPopulateRSS(PROT_NONE, &before, &after); + + // Mapping not committed. + EXPECT_NEAR(before, after, kRSSTolerance); +} + +// Returns the calling thread's name. +PosixErrorOr ThreadName() { + // "The buffer should allow space for up to 16 bytes; the returned std::string + // will be null-terminated if it is shorter than that." - prctl(2). But we + // always want the thread name to be null-terminated. + char thread_name[17]; + int rc = prctl(PR_GET_NAME, thread_name, 0, 0, 0); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "prctl(PR_GET_NAME)"); + } + thread_name[16] = '\0'; + return std::string(thread_name); +} + +// Parses the contents of a /proc/[pid]/status file into a collection of +// key-value pairs. +PosixErrorOr> ParseProcStatus( + absl::string_view status_str) { + std::map fields; + for (absl::string_view const line : + absl::StrSplit(status_str, '\n', absl::SkipWhitespace())) { + const std::pair kv = + absl::StrSplit(line, absl::MaxSplits(":\t", 1)); + if (kv.first.empty()) { + return PosixError( + EINVAL, absl::StrCat("failed to parse key in line \"", line, "\"")); + } + std::string key(kv.first); + if (fields.count(key)) { + return PosixError(EINVAL, + absl::StrCat("duplicate key \"", kv.first, "\"")); + } + std::string value(kv.second); + absl::StripLeadingAsciiWhitespace(&value); + fields.emplace(std::move(key), std::move(value)); + } + return fields; +} + +TEST(ParseProcStatusTest, ParsesSimpleStatusFileWithMixedWhitespaceCorrectly) { + EXPECT_THAT( + ParseProcStatus( + "Name:\tinit\nState:\tS (sleeping)\nCapEff:\t 0000001fffffffff\n"), + IsPosixErrorOkAndHolds(UnorderedElementsAre( + Pair("Name", "init"), Pair("State", "S (sleeping)"), + Pair("CapEff", "0000001fffffffff")))); +} + +TEST(ParseProcStatusTest, DetectsDuplicateKeys) { + auto proc_status_or = ParseProcStatus("Name:\tfoo\nName:\tfoo\n"); + EXPECT_THAT(proc_status_or, + PosixErrorIs(EINVAL, ::testing::StrEq("duplicate key \"Name\""))); +} + +TEST(ParseProcStatusTest, DetectsMissingTabs) { + EXPECT_THAT(ParseProcStatus("Name:foo\nPid: 1\n"), + IsPosixErrorOkAndHolds(UnorderedElementsAre(Pair("Name:foo", ""), + Pair("Pid: 1", "")))); +} + +TEST(ProcPidStatusTest, HasBasicFields) { + // Do this on a separate thread since we want tgid != tid. + ScopedThread([] { + const pid_t tgid = getpid(); + const pid_t tid = syscall(SYS_gettid); + EXPECT_NE(tgid, tid); + const auto thread_name = ASSERT_NO_ERRNO_AND_VALUE(ThreadName()); + + std::string status_str = ASSERT_NO_ERRNO_AND_VALUE( + GetContents(absl::StrCat("/proc/", tid, "/status"))); + + ASSERT_FALSE(status_str.empty()); + const auto status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(status_str)); + EXPECT_THAT(status, IsSupersetOf({Pair("Name", thread_name), + Pair("Tgid", absl::StrCat(tgid)), + Pair("Pid", absl::StrCat(tid)), + Pair("PPid", absl::StrCat(getppid()))})); + }); +} + +TEST(ProcPidStatusTest, StateRunning) { + // Task must be running when reading the file. + const pid_t tid = syscall(SYS_gettid); + std::string status_str = ASSERT_NO_ERRNO_AND_VALUE( + GetContents(absl::StrCat("/proc/", tid, "/status"))); + + EXPECT_THAT(ParseProcStatus(status_str), + IsPosixErrorOkAndHolds(Contains(Pair("State", "R (running)")))); +} + +TEST(ProcPidStatusTest, StateSleeping_NoRandomSave) { + // Starts a child process that blocks and checks that State is sleeping. + auto res = WithSubprocess( + [&](int pid) -> PosixError { + // Because this test is timing based we will disable cooperative saving + // and the test itself also has random saving disabled. + const DisableSave ds; + // Try multiple times in case the child isn't sleeping when status file + // is read. + MonotonicTimer timer; + timer.Start(); + for (;;) { + ASSIGN_OR_RETURN_ERRNO( + std::string status_str, + GetContents(absl::StrCat("/proc/", pid, "/status"))); + ASSIGN_OR_RETURN_ERRNO(auto map, ParseProcStatus(status_str)); + if (map["State"] == std::string("S (sleeping)")) { + // Test passed! + return NoError(); + } + if (timer.Duration() > absl::Seconds(10)) { + return PosixError(ETIMEDOUT, "Timeout waiting for child to sleep"); + } + absl::SleepFor(absl::Milliseconds(10)); + } + }, + nullptr, nullptr); + ASSERT_NO_ERRNO(res); +} + +TEST(ProcPidStatusTest, ValuesAreTabDelimited) { + std::string status_str = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/status")); + ASSERT_FALSE(status_str.empty()); + for (absl::string_view const line : + absl::StrSplit(status_str, '\n', absl::SkipWhitespace())) { + EXPECT_NE(std::string::npos, line.find(":\t")); + } +} + +// Threads properly counts running threads. +// +// TODO: Test zombied threads while the thread group leader is still +// running with generalized fork and clone children from the wait test. +TEST(ProcPidStatusTest, Threads) { + char buf[4096] = {}; + EXPECT_THAT(ReadWhileRunning("status", buf, sizeof(buf) - 1), + SyscallSucceedsWithValue(Gt(0))); + + auto status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(buf)); + auto it = status.find("Threads"); + ASSERT_NE(it, status.end()); + int threads = -1; + EXPECT_TRUE(absl::SimpleAtoi(it->second, &threads)) + << "Threads value " << it->second << " is not a number"; + // Don't make assumptions about the exact number of threads, as it may not be + // constant. + EXPECT_GE(threads, 1); + + memset(buf, 0, sizeof(buf)); + EXPECT_THAT(ReadWhileZombied("status", buf, sizeof(buf) - 1), + SyscallSucceedsWithValue(Gt(0))); + + status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(buf)); + it = status.find("Threads"); + ASSERT_NE(it, status.end()); + threads = -1; + EXPECT_TRUE(absl::SimpleAtoi(it->second, &threads)) + << "Threads value " << it->second << " is not a number"; + // There must be only the thread group leader remaining, zombied. + EXPECT_EQ(threads, 1); +} + +// Returns true if all characters in s are digits. +bool IsDigits(absl::string_view s) { + return std::all_of(s.begin(), s.end(), absl::ascii_isdigit); +} + +TEST(ProcPidStatTest, VSSRSS) { + std::string status_str = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/status")); + ASSERT_FALSE(status_str.empty()); + auto status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(status_str)); + + const auto vss_it = status.find("VmSize"); + ASSERT_NE(vss_it, status.end()); + + absl::string_view vss_str(vss_it->second); + + // Room for the " kB" suffix plus at least one digit. + ASSERT_GT(vss_str.length(), 3); + EXPECT_TRUE(absl::EndsWith(vss_str, " kB")); + // Everything else is part of a number. + EXPECT_TRUE(IsDigits(vss_str.substr(0, vss_str.length() - 3))) << vss_str; + // ... which is not 0. + EXPECT_NE('0', vss_str[0]); + + const auto rss_it = status.find("VmRSS"); + ASSERT_NE(rss_it, status.end()); + + absl::string_view rss_str(rss_it->second); + + // Room for the " kB" suffix plus at least one digit. + ASSERT_GT(rss_str.length(), 3); + EXPECT_TRUE(absl::EndsWith(rss_str, " kB")); + // Everything else is part of a number. + EXPECT_TRUE(IsDigits(rss_str.substr(0, rss_str.length() - 3))) << rss_str; + // ... which is not 0. + EXPECT_NE('0', rss_str[0]); +} + +// Parse an array of NUL-terminated char* arrays, returning a vector of strings. +std::vector ParseNulTerminatedStrings(std::string contents) { + EXPECT_EQ('\0', contents.back()); + // The split will leave an empty std::string if the NUL-byte remains, so pop it. + contents.pop_back(); + + return absl::StrSplit(contents, '\0'); +} + +TEST(ProcPidCmdline, MatchesArgv) { + std::vector proc_cmdline = ParseNulTerminatedStrings( + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/cmdline"))); + EXPECT_THAT(saved_argv, ContainerEq(proc_cmdline)); +} + +TEST(ProcPidEnviron, MatchesEnviron) { + std::vector proc_environ = ParseNulTerminatedStrings( + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/environ"))); + // Get the environment from the environ variable, which we will compare with + // /proc/self/environ. + std::vector env; + for (char** v = environ; *v; v++) { + env.push_back(*v); + } + EXPECT_THAT(env, ContainerEq(proc_environ)); +} + +TEST(ProcPidCmdline, SubprocessForkSameCmdline) { + std::vector proc_cmdline_parent; + std::vector proc_cmdline; + proc_cmdline_parent = ParseNulTerminatedStrings( + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/cmdline"))); + auto res = WithSubprocess( + [&](int pid) -> PosixError { + ASSIGN_OR_RETURN_ERRNO( + auto raw_cmdline, + GetContents(absl::StrCat("/proc/", pid, "/cmdline"))); + proc_cmdline = ParseNulTerminatedStrings(raw_cmdline); + return NoError(); + }, + nullptr, nullptr); + ASSERT_NO_ERRNO(res); + + for (size_t i = 0; i < proc_cmdline_parent.size(); i++) { + EXPECT_EQ(proc_cmdline_parent[i], proc_cmdline[i]); + } +} + +// Test whether /proc/PID/ symlinks can be read for a running process. +TEST(ProcPidSymlink, SubprocessRunning) { + char buf[1]; + + EXPECT_THAT(ReadlinkWhileRunning("exe", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadlinkWhileRunning("ns/net", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadlinkWhileRunning("ns/pid", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadlinkWhileRunning("ns/user", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); +} +// FIXME: Inconsistent behavior between gVisor and linux +// on proc files. +TEST(ProcPidSymlink, SubprocessZombied) { + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + char buf[1]; + + int want = EACCES; + if (!IsRunningOnGvisor()) { + auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); + if (version.major == 4 && version.minor > 3) { + want = ENOENT; + } + } + + EXPECT_THAT(ReadlinkWhileZombied("exe", buf, sizeof(buf)), + SyscallFailsWithErrno(want)); + + if (!IsRunningOnGvisor()) { + EXPECT_THAT(ReadlinkWhileZombied("ns/net", buf, sizeof(buf)), + SyscallFailsWithErrno(want)); + } + + // FIXME: Inconsistent behavior between gVisor and linux + // on proc files. + // 4.17 & gVisor: Syscall succeeds and returns 1 + // EXPECT_THAT(ReadlinkWhileZombied("ns/pid", buf, sizeof(buf)), + // SyscallFailsWithErrno(EACCES)); + + // FIXME: Inconsistent behavior between gVisor and linux + // on proc files. + // 4.17 & gVisor: Syscall succeeds and returns 1. + // EXPECT_THAT(ReadlinkWhileZombied("ns/user", buf, sizeof(buf)), + // SyscallFailsWithErrno(EACCES)); +} + +// Test whether /proc/PID/ symlinks can be read for an exited process. +TEST(ProcPidSymlink, SubprocessExited) { + // FIXME: These all succeed on gVisor. + SKIP_IF(IsRunningOnGvisor()); + + char buf[1]; + + EXPECT_THAT(ReadlinkWhileExited("exe", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + + EXPECT_THAT(ReadlinkWhileExited("ns/net", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + + EXPECT_THAT(ReadlinkWhileExited("ns/pid", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + + EXPECT_THAT(ReadlinkWhileExited("ns/user", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); +} + +// /proc/PID/exe points to the correct binary. +TEST(ProcPidExe, Subprocess) { + auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe")); + auto expected_absolute_path = + ASSERT_NO_ERRNO_AND_VALUE(MakeAbsolute(link, "")); + + char actual[PATH_MAX + 1] = {}; + ASSERT_THAT(ReadlinkWhileRunning("exe", actual, sizeof(actual)), + SyscallSucceedsWithValue(Gt(0))); + EXPECT_EQ(actual, expected_absolute_path); +} + +// Test whether /proc/PID/ files can be read for a running process. +TEST(ProcPidFile, SubprocessRunning) { + char buf[1]; + + EXPECT_THAT(ReadWhileRunning("auxv", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("cmdline", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("comm", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("gid_map", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("io", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("maps", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("stat", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("status", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileRunning("uid_map", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); +} + +// Test whether /proc/PID/ files can be read for a zombie process. +TEST(ProcPidFile, SubprocessZombie) { + char buf[1]; + // 4.17: Succeeds and returns 1 + // gVisor: Succeds and returns 0 + EXPECT_THAT(ReadWhileZombied("auxv", buf, sizeof(buf)), SyscallSucceeds()); + + EXPECT_THAT(ReadWhileZombied("cmdline", buf, sizeof(buf)), + SyscallSucceedsWithValue(0)); + + EXPECT_THAT(ReadWhileZombied("comm", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileZombied("gid_map", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileZombied("maps", buf, sizeof(buf)), + SyscallSucceedsWithValue(0)); + + EXPECT_THAT(ReadWhileZombied("stat", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileZombied("status", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(ReadWhileZombied("uid_map", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // FIXME: Inconsistent behavior between gVisor and linux + // on proc files. + // gVisor & 4.17: Succeeds and returns 1. + // EXPECT_THAT(ReadWhileZombied("io", buf, sizeof(buf)), + // SyscallFailsWithErrno(EACCES)); +} + +// Test whether /proc/PID/ files can be read for an exited process. +TEST(ProcPidFile, SubprocessExited) { + char buf[1]; + + // FIXME: Inconsistent behavior between kernels + // gVisor: Fails with ESRCH. + // 4.17: Succeeds and returns 1. + // EXPECT_THAT(ReadWhileExited("auxv", buf, sizeof(buf)), + // SyscallFailsWithErrno(ESRCH)); + + EXPECT_THAT(ReadWhileExited("cmdline", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + + if (!IsRunningOnGvisor()) { + // FIXME: Succeeds on gVisor. + EXPECT_THAT(ReadWhileExited("comm", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + } + + EXPECT_THAT(ReadWhileExited("gid_map", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + if (!IsRunningOnGvisor()) { + // FIXME: Succeeds on gVisor. + EXPECT_THAT(ReadWhileExited("io", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + } + + if (!IsRunningOnGvisor()) { + // FIXME: Returns EOF on gVisor. + EXPECT_THAT(ReadWhileExited("maps", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + } + + if (!IsRunningOnGvisor()) { + // FIXME: Succeeds on gVisor. + EXPECT_THAT(ReadWhileExited("stat", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + } + + if (!IsRunningOnGvisor()) { + // FIXME: Succeeds on gVisor. + EXPECT_THAT(ReadWhileExited("status", buf, sizeof(buf)), + SyscallFailsWithErrno(ESRCH)); + } + + EXPECT_THAT(ReadWhileExited("uid_map", buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); +} + +PosixError DirContainsImpl(absl::string_view path, + const std::vector& targets, bool strict) { + ASSIGN_OR_RETURN_ERRNO(auto listing, ListDir(path, false)); + bool success = true; + + for (auto& expected_entry : targets) { + auto cursor = std::find(listing.begin(), listing.end(), expected_entry); + if (cursor == listing.end()) { + success = false; + } + } + + if (!success) { + return PosixError( + ENOENT, + absl::StrCat("Failed to find one or more paths in '", path, "'")); + } + + if (strict) { + if (targets.size() != listing.size()) { + return PosixError( + EINVAL, + absl::StrCat("Expected to find ", targets.size(), " elements in '", + path, "', but found ", listing.size())); + } + } + + return NoError(); +} + +PosixError DirContains(absl::string_view path, + const std::vector& targets) { + return DirContainsImpl(path, targets, false); +} + +PosixError DirContainsExactly(absl::string_view path, + const std::vector& targets) { + return DirContainsImpl(path, targets, true); +} + +PosixError EventuallyDirContainsExactly(absl::string_view path, + const std::vector& targets) { + constexpr int kRetryCount = 100; + const absl::Duration kRetryDelay = absl::Milliseconds(100); + + for (int i = 0; i < kRetryCount; ++i) { + auto res = DirContainsExactly(path, targets); + if (res.ok()) { + return res; + } else if (i < kRetryCount - 1) { + // Sleep if this isn't the final iteration. + absl::SleepFor(kRetryDelay); + } + } + return PosixError(ETIMEDOUT, + "Timed out while waiting for directory to contain files "); +} + +TEST(ProcTask, Basic) { + EXPECT_NO_ERRNO( + DirContains("/proc/self/task", {".", "..", absl::StrCat(getpid())})); +} + +std::vector TaskFiles(const std::vector& initial_contents, + const std::vector& pids) { + return VecCat( + initial_contents, + ApplyVec([](const pid_t p) { return absl::StrCat(p); }, pids)); +} + +std::vector TaskFiles(const std::vector& pids) { + return TaskFiles({".", "..", absl::StrCat(getpid())}, pids); +} + +// Helper class for creating a new task in the current thread group. +class BlockingChild { + public: + BlockingChild() : thread_([=] { Start(); }) {} + ~BlockingChild() { Join(); } + + pid_t Tid() const { + absl::MutexLock ml(&mu_); + mu_.Await(absl::Condition(&tid_ready_)); + return tid_; + } + + void Join() { Stop(); } + + private: + void Start() { + absl::MutexLock ml(&mu_); + tid_ = syscall(__NR_gettid); + tid_ready_ = true; + mu_.Await(absl::Condition(&stop_)); + } + + void Stop() { + absl::MutexLock ml(&mu_); + stop_ = true; + } + + mutable absl::Mutex mu_; + bool stop_ GUARDED_BY(mu_) = false; + pid_t tid_; + bool tid_ready_ GUARDED_BY(mu_) = false; + + // Must be last to ensure that the destructor for the thread is run before + // any other member of the object is destroyed. + ScopedThread thread_; +}; + +TEST(ProcTask, NewThreadAppears) { + auto initial = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/task", false)); + BlockingChild child1; + EXPECT_NO_ERRNO(DirContainsExactly("/proc/self/task", + TaskFiles(initial, {child1.Tid()}))); +} + +TEST(ProcTask, KilledThreadsDisappear) { + auto initial = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/task/", false)); + + BlockingChild child1; + EXPECT_NO_ERRNO(DirContainsExactly("/proc/self/task", + TaskFiles(initial, {child1.Tid()}))); + + // Stat child1's task file. + struct stat statbuf; + const std::string child1_task_file = + absl::StrCat("/proc/self/task/", child1.Tid()); + EXPECT_THAT(stat(child1_task_file.c_str(), &statbuf), SyscallSucceeds()); + + BlockingChild child2; + EXPECT_NO_ERRNO(DirContainsExactly( + "/proc/self/task", TaskFiles(initial, {child1.Tid(), child2.Tid()}))); + + BlockingChild child3; + BlockingChild child4; + BlockingChild child5; + EXPECT_NO_ERRNO(DirContainsExactly( + "/proc/self/task", + TaskFiles(initial, {child1.Tid(), child2.Tid(), child3.Tid(), + child4.Tid(), child5.Tid()}))); + + child2.Join(); + EXPECT_NO_ERRNO(EventuallyDirContainsExactly( + "/proc/self/task", TaskFiles(initial, {child1.Tid(), child3.Tid(), + child4.Tid(), child5.Tid()}))); + + child1.Join(); + child4.Join(); + EXPECT_NO_ERRNO(EventuallyDirContainsExactly( + "/proc/self/task", TaskFiles(initial, {child3.Tid(), child5.Tid()}))); + + // Stat child1's task file again. This time it should fail. + EXPECT_THAT(stat(child1_task_file.c_str(), &statbuf), + SyscallFailsWithErrno(ENOENT)); + + child3.Join(); + child5.Join(); + EXPECT_NO_ERRNO(EventuallyDirContainsExactly("/proc/self/task", initial)); +} + +TEST(ProcTask, ChildTaskDir) { + BlockingChild child1; + EXPECT_NO_ERRNO(DirContains("/proc/self/task", TaskFiles({child1.Tid()}))); + EXPECT_NO_ERRNO(DirContains(absl::StrCat("/proc/", child1.Tid(), "/task"), + TaskFiles({child1.Tid()}))); +} + +PosixError VerifyPidDir(std::string path) { + return DirContains(path, {"exe", "fd", "io", "maps", "ns", "stat", "status"}); +} + +TEST(ProcTask, VerifyTaskDir) { + EXPECT_NO_ERRNO(VerifyPidDir("/proc/self")); + + EXPECT_NO_ERRNO(VerifyPidDir(absl::StrCat("/proc/self/task/", getpid()))); + BlockingChild child1; + EXPECT_NO_ERRNO(VerifyPidDir(absl::StrCat("/proc/self/task/", child1.Tid()))); + + // Only the first level of task directories should contain the 'task' + // directory. That is: + // + // /proc/1234/task <- should exist + // /proc/1234/task/1234/task <- should not exist + // /proc/1234/task/1235/task <- should not exist (where 1235 is in the same + // thread group as 1234). + EXPECT_FALSE( + DirContains(absl::StrCat("/proc/self/task/", getpid()), {"task"}).ok()) + << "Found 'task' directory in an inner directory."; +} + +TEST(ProcTask, TaskDirCannotBeDeleted) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + + EXPECT_THAT(rmdir("/proc/self/task"), SyscallFails()); + EXPECT_THAT(rmdir(absl::StrCat("/proc/self/task/", getpid()).c_str()), + SyscallFailsWithErrno(EACCES)); +} + +TEST(ProcTask, TaskDirHasCorrectMetadata) { + struct stat st; + EXPECT_THAT(stat("/proc/self/task", &st), SyscallSucceeds()); + EXPECT_TRUE(S_ISDIR(st.st_mode)); + + // Verify file is readable and executable by everyone. + mode_t expected_permissions = + S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + mode_t permissions = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + EXPECT_EQ(expected_permissions, permissions); +} + +TEST(ProcTask, TaskDirCanSeekToEnd) { + const FileDescriptor dirfd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/task", O_RDONLY)); + EXPECT_THAT(lseek(dirfd.get(), 0, SEEK_END), SyscallSucceeds()); +} + +TEST(ProcTask, VerifyTaskDirNlinks) { + // A task directory will have 3 links if the taskgroup has a single + // thread. For example, the following shows where the links to + // '/proc/12345/task comes' from for a single threaded process with pid 12345: + // + // /proc/12345/task <-- 1 link for the directory itself + // . <-- link from "." + // .. + // 12345 + // . + // .. <-- link from ".." to parent. + // + // + // We can't assert an absolute number of links since we don't control how many + // threads the test framework spawns. Instead, we'll ensure creating a new + // thread increases the number of links as expected. + + // Once we reach the test body, we can count on the thread count being stable + // unless we spawn a new one. + uint64_t initial_links = ASSERT_NO_ERRNO_AND_VALUE(Links("/proc/self/task")); + ASSERT_GE(initial_links, 3); + + // For each new subtask, we should gain a new link. + BlockingChild child1; + EXPECT_THAT(Links("/proc/self/task"), + IsPosixErrorOkAndHolds(initial_links + 1)); + BlockingChild child2; + EXPECT_THAT(Links("/proc/self/task"), + IsPosixErrorOkAndHolds(initial_links + 2)); +} + +TEST(ProcTask, CommContainsThreadNameAndTrailingNewline) { + constexpr char kThreadName[] = "TestThread12345"; + ASSERT_THAT(prctl(PR_SET_NAME, kThreadName), SyscallSucceeds()); + + auto thread_name = ASSERT_NO_ERRNO_AND_VALUE( + GetContents(JoinPath("/proc", absl::StrCat(getpid()), "task", + absl::StrCat(syscall(SYS_gettid)), "comm"))); + EXPECT_EQ(absl::StrCat(kThreadName, "\n"), thread_name); +} + +TEST(ProcTaskNs, NsDirExistsAndHasCorrectMetadata) { + EXPECT_NO_ERRNO(DirContains("/proc/self/ns", {"net", "pid", "user"})); + + // Let's just test the 'pid' entry, all of them are very similar. + struct stat st; + EXPECT_THAT(lstat("/proc/self/ns/pid", &st), SyscallSucceeds()); + EXPECT_TRUE(S_ISLNK(st.st_mode)); + + auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/ns/pid")); + EXPECT_THAT(link, ::testing::StartsWith("pid:[")); +} + +TEST(ProcTaskNs, AccessOnNsNodeSucceeds) { + EXPECT_THAT(access("/proc/self/ns/pid", F_OK), SyscallSucceeds()); +} + +TEST(ProcSysKernelHostname, Exists) { + EXPECT_THAT(open("/proc/sys/kernel/hostname", O_RDONLY), SyscallSucceeds()); +} + +TEST(ProcSysKernelHostname, MatchesUname) { + struct utsname buf; + EXPECT_THAT(uname(&buf), SyscallSucceeds()); + const std::string hostname = absl::StrCat(buf.nodename, "\n"); + auto procfs_hostname = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/hostname")); + EXPECT_EQ(procfs_hostname, hostname); +} + +TEST(ProcSysVmMmapMinAddr, HasNumericValue) { + const std::string mmap_min_addr_str = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/vm/mmap_min_addr")); + uintptr_t mmap_min_addr; + EXPECT_TRUE(absl::SimpleAtoi(mmap_min_addr_str, &mmap_min_addr)) + << "/proc/sys/vm/mmap_min_addr does not contain a numeric value: " + << mmap_min_addr_str; +} + +TEST(ProcSysVmOvercommitMemory, HasNumericValue) { + const std::string overcommit_memory_str = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/vm/overcommit_memory")); + uintptr_t overcommit_memory; + EXPECT_TRUE(absl::SimpleAtoi(overcommit_memory_str, &overcommit_memory)) + << "/proc/sys/vm/overcommit_memory does not contain a numeric value: " + << overcommit_memory; +} + +// Check that link for proc fd entries point the target node, not the +// symlink itself. +TEST(ProcTaskFd, FstatatFollowsSymlink) { + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + + struct stat sproc = {}; + EXPECT_THAT( + fstatat(-1, absl::StrCat("/proc/self/fd/", fd.get()).c_str(), &sproc, 0), + SyscallSucceeds()); + + struct stat sfile = {}; + EXPECT_THAT(fstatat(-1, file.path().c_str(), &sfile, 0), SyscallSucceeds()); + + // If fstatat follows the fd symlink, the device and inode numbers should + // match at a minimum. + EXPECT_EQ(sproc.st_dev, sfile.st_dev); + EXPECT_EQ(sproc.st_ino, sfile.st_ino); + EXPECT_EQ(0, memcmp(&sfile, &sproc, sizeof(sfile))); +} + +TEST(ProcFilesystems, Bug65172365) { + std::string proc_filesystems = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/filesystems")); + ASSERT_FALSE(proc_filesystems.empty()); +} + +TEST(ProcFilesystems, PresenceOfShmMaxMniAll) { + uint64_t shmmax = 0; + uint64_t shmall = 0; + uint64_t shmmni = 0; + std::string proc_file; + proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmax")); + ASSERT_FALSE(proc_file.empty()); + ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmax)); + proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmall")); + ASSERT_FALSE(proc_file.empty()); + ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmall)); + proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmni")); + ASSERT_FALSE(proc_file.empty()); + ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmni)); + + ASSERT_GT(shmmax, 0); + ASSERT_GT(shmall, 0); + ASSERT_GT(shmmni, 0); + ASSERT_LE(shmall, shmmax); + + // These values should never be higher than this by default, for more + // information see uapi/linux/shm.h + ASSERT_LE(shmmax, ULONG_MAX - (1UL << 24)); + ASSERT_LE(shmall, ULONG_MAX - (1UL << 24)); +} + +// Check that /proc/mounts is a symlink to self/mounts. +TEST(ProcMounts, IsSymlink) { + auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/mounts")); + EXPECT_EQ(link, "self/mounts"); +} + +// Check that /proc/self/mounts looks something like a real mounts file. +TEST(ProcSelfMounts, RequiredFieldsArePresent) { + auto mounts = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/mounts")); + EXPECT_THAT(mounts, + AllOf( + // Root mount. + ContainsRegex(R"(\S+ / \S+ (rw|ro)\S* [0-9]+ [0-9]+\s)"), + // Root mount. + ContainsRegex(R"(\S+ /proc \S+ rw\S* [0-9]+ [0-9]+\s)"))); +} +} // namespace +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + for (int i = 0; i < argc; ++i) { + gvisor::testing::saved_argv.emplace_back(std::string(argv[i])); + } + + gvisor::testing::TestInit(&argc, &argv); + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc new file mode 100644 index 000000000..6060d0644 --- /dev/null +++ b/test/syscalls/linux/proc_net.cc @@ -0,0 +1,59 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +TEST(ProcNetIfInet6, Format) { + auto ifinet6 = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/if_inet6")); + EXPECT_THAT(ifinet6, + ::testing::MatchesRegex( + // Ex: "00000000000000000000000000000001 01 80 10 80 lo\n" + "^([a-f\\d]{32}( [a-f\\d]{2}){4} +[a-z][a-z\\d]*\\n)+$")); +} + +TEST(ProcSysNetIpv4Sack, Exists) { + EXPECT_THAT(open("/proc/sys/net/ipv4/tcp_sack", O_RDONLY), SyscallSucceeds()); +} + +TEST(ProcSysNetIpv4Sack, CanReadAndWrite) { + auto const fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/net/ipv4/tcp_sack", O_RDWR)); + + char buf; + EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected tcp_sack: " << buf; + + char to_write = (buf == '1') ? '0' : '1'; + EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0), + SyscallSucceedsWithValue(sizeof(to_write))); + + buf = 0; + EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), + SyscallSucceedsWithValue(sizeof(buf))); + EXPECT_EQ(buf, to_write); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/pselect.cc b/test/syscalls/linux/pselect.cc new file mode 100644 index 000000000..3294f6c14 --- /dev/null +++ b/test/syscalls/linux/pselect.cc @@ -0,0 +1,190 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/base_poll_test.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +struct MaskWithSize { + sigset_t* mask; + size_t mask_size; +}; + +// Linux and glibc have a different idea of the sizeof sigset_t. When calling +// the syscall directly, use what the kernel expects. +unsigned kSigsetSize = SIGRTMAX / 8; + +// Linux pselect(2) differs from the glibc wrapper function in that Linux +// updates the timeout with the amount of time remaining. In order to test this +// behavior we need to use the syscall directly. +int syscallPselect6(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, struct timespec* timeout, + const MaskWithSize* mask_with_size) { + return syscall(SYS_pselect6, nfds, readfds, writefds, exceptfds, timeout, + mask_with_size); +} + +class PselectTest : public BasePollTest { + protected: + void SetUp() override { BasePollTest::SetUp(); } + void TearDown() override { BasePollTest::TearDown(); } +}; + +// See that when there are no FD sets, pselect behaves like sleep. +TEST_F(PselectTest, NullFds) { + struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); + ASSERT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, nullptr), + SyscallSucceeds()); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_nsec, 0); + + timeout = absl::ToTimespec(absl::Milliseconds(10)); + ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), + SyscallSucceeds()); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_nsec, 0); +} + +TEST_F(PselectTest, ClosedFds) { + fd_set read_set; + FD_ZERO(&read_set); + int fd; + ASSERT_THAT(fd = dup(1), SyscallSucceeds()); + ASSERT_THAT(close(fd), SyscallSucceeds()); + FD_SET(fd, &read_set); + struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); + EXPECT_THAT( + syscallPselect6(fd + 1, &read_set, nullptr, nullptr, &timeout, nullptr), + SyscallFailsWithErrno(EBADF)); +} + +TEST_F(PselectTest, ZeroTimeout) { + struct timespec timeout = {}; + ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), + SyscallSucceeds()); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_nsec, 0); +} + +// If random S/R interrupts the pselect, SIGALRM may be delivered before pselect +// restarts, causing the pselect to hang forever. +TEST_F(PselectTest, NoTimeout_NoRandomSave) { + // When there's no timeout, pselect may never return so set a timer. + SetTimer(absl::Milliseconds(100)); + // See that we get interrupted by the timer. + ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, nullptr, nullptr), + SyscallFailsWithErrno(EINTR)); + EXPECT_TRUE(TimerFired()); +} + +TEST_F(PselectTest, InvalidTimeoutNegative) { + struct timespec timeout = absl::ToTimespec(absl::Seconds(-1)); + ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), + SyscallFailsWithErrno(EINVAL)); + EXPECT_EQ(timeout.tv_sec, -1); + EXPECT_EQ(timeout.tv_nsec, 0); +} + +TEST_F(PselectTest, InvalidTimeoutNotNormalized) { + struct timespec timeout = {0, 1000000001}; + ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), + SyscallFailsWithErrno(EINVAL)); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_nsec, 1000000001); +} + +TEST_F(PselectTest, EmptySigMaskInvalidMaskSize) { + struct timespec timeout = {}; + MaskWithSize invalid = {nullptr, 7}; + EXPECT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, &invalid), + SyscallSucceeds()); +} + +TEST_F(PselectTest, EmptySigMaskValidMaskSize) { + struct timespec timeout = {}; + MaskWithSize invalid = {nullptr, 8}; + EXPECT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, &invalid), + SyscallSucceeds()); +} + +TEST_F(PselectTest, InvalidMaskSize) { + struct timespec timeout = {}; + sigset_t sigmask; + ASSERT_THAT(sigemptyset(&sigmask), SyscallSucceeds()); + MaskWithSize invalid = {&sigmask, 7}; + EXPECT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &invalid), + SyscallFailsWithErrno(EINVAL)); +} + +// Verify that signals blocked by the pselect mask (that would otherwise be +// allowed) do not interrupt pselect. +TEST_F(PselectTest, SignalMaskBlocksSignal) { + absl::Duration duration(absl::Seconds(30)); + struct timespec timeout = absl::ToTimespec(duration); + absl::Duration timer_duration(absl::Seconds(10)); + + // Call with a mask that blocks SIGALRM. See that pselect is not interrupted + // (i.e. returns 0) and that upon completion, the timer has fired. + sigset_t mask; + ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); + ASSERT_THAT(sigaddset(&mask, SIGALRM), SyscallSucceeds()); + MaskWithSize mask_with_size = {&mask, kSigsetSize}; + SetTimer(timer_duration); + MaybeSave(); + ASSERT_FALSE(TimerFired()); + ASSERT_THAT( + syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &mask_with_size), + SyscallSucceeds()); + EXPECT_TRUE(TimerFired()); + EXPECT_EQ(absl::DurationFromTimespec(timeout), absl::Duration()); +} + +// Verify that signals allowed by the pselect mask (that would otherwise be +// blocked) interrupt pselect. +TEST_F(PselectTest, SignalMaskAllowsSignal) { + absl::Duration duration = absl::Seconds(30); + struct timespec timeout = absl::ToTimespec(duration); + absl::Duration timer_duration = absl::Seconds(10); + + sigset_t mask; + ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); + + // Block SIGALRM. + auto cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, SIGALRM)); + + // Call with a mask that unblocks SIGALRM. See that pselect is interrupted. + MaskWithSize mask_with_size = {&mask, kSigsetSize}; + SetTimer(timer_duration); + MaybeSave(); + ASSERT_FALSE(TimerFired()); + ASSERT_THAT( + syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &mask_with_size), + SyscallFailsWithErrno(EINTR)); + EXPECT_TRUE(TimerFired()); + EXPECT_GT(absl::DurationFromTimespec(timeout), absl::Duration()); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc new file mode 100644 index 000000000..d3b3b8b02 --- /dev/null +++ b/test/syscalls/linux/ptrace.cc @@ -0,0 +1,948 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/logging.h" +#include "test/util/multiprocess_util.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Sends sig to the current process with tgkill(2). +// +// glibc's raise(2) may change the signal mask before sending the signal. These +// extra syscalls make tests of syscall, signal interception, etc. difficult to +// write. +void RaiseSignal(int sig) { + pid_t pid = getpid(); + TEST_PCHECK(pid > 0); + pid_t tid = gettid(); + TEST_PCHECK(tid > 0); + TEST_PCHECK(tgkill(pid, tid, sig) == 0); +} + +// Returns the Yama ptrace scope. +PosixErrorOr YamaPtraceScope() { + constexpr char kYamaPtraceScopePath[] = "/proc/sys/kernel/yama/ptrace_scope"; + + ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(kYamaPtraceScopePath)); + if (!exists) { + // File doesn't exist means no Yama, so the scope is disabled -> 0. + return 0; + } + + std::string contents; + RETURN_IF_ERRNO(GetContents(kYamaPtraceScopePath, &contents)); + + int scope; + if (!absl::SimpleAtoi(contents, &scope)) { + return PosixError(EINVAL, absl::StrCat(contents, ": not a valid number")); + } + + return scope; +} + +TEST(PtraceTest, AttachSelf) { + EXPECT_THAT(ptrace(PTRACE_ATTACH, gettid(), 0, 0), + SyscallFailsWithErrno(EPERM)); +} + +TEST(PtraceTest, AttachSameThreadGroup) { + pid_t const tid = gettid(); + ScopedThread([&] { + EXPECT_THAT(ptrace(PTRACE_ATTACH, tid, 0, 0), SyscallFailsWithErrno(EPERM)); + }); +} + +TEST(PtraceTest, AttachParent_PeekData_PokeData_SignalSuppression) { + // Yama prevents attaching to a parent. Skip the test if the scope is anything + // except disabled. + SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) > 0); + + constexpr long kBeforePokeDataValue = 10; + constexpr long kAfterPokeDataValue = 20; + + volatile long word = kBeforePokeDataValue; + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Attach to the parent. + pid_t const parent_pid = getppid(); + TEST_PCHECK(ptrace(PTRACE_ATTACH, parent_pid, 0, 0) == 0); + MaybeSave(); + + // Block until the parent enters signal-delivery-stop as a result of the + // SIGSTOP sent by PTRACE_ATTACH. + int status; + TEST_PCHECK(waitpid(parent_pid, &status, 0) == parent_pid); + MaybeSave(); + TEST_CHECK(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); + + // Replace the value of word in the parent process with kAfterPokeDataValue. + long const parent_word = ptrace(PTRACE_PEEKDATA, parent_pid, &word, 0); + MaybeSave(); + TEST_CHECK(parent_word == kBeforePokeDataValue); + TEST_PCHECK( + ptrace(PTRACE_POKEDATA, parent_pid, &word, kAfterPokeDataValue) == 0); + MaybeSave(); + + // Detach from the parent and suppress the SIGSTOP. If the SIGSTOP is not + // suppressed, the parent will hang in group-stop, causing the test to time + // out. + TEST_PCHECK(ptrace(PTRACE_DETACH, parent_pid, 0, 0) == 0); + MaybeSave(); + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to complete. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; + + // Check that the child's PTRACE_POKEDATA was effective. + EXPECT_EQ(kAfterPokeDataValue, word); +} + +TEST(PtraceTest, GetSigMask) { + // doesn't define these until Linux 4.4, even though the features + // were added in 3.11. + constexpr auto kPtraceGetSigMask = static_cast(0x420a); + constexpr auto kPtraceSetSigMask = static_cast(0x420b); + // glibc and the Linux kernel define a sigset_t with different sizes. To avoid + // creating a kernel_sigset_t and recreating all the modification functions + // (sigemptyset, etc), we just hardcode the kernel sigset size. + constexpr int kSizeofKernelSigset = 8; + constexpr int kBlockSignal = SIGUSR1; + sigset_t blocked; + sigemptyset(&blocked); + sigaddset(&blocked, kBlockSignal); + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Install a signal handler for kBlockSignal to avoid termination and block + // it. + TEST_PCHECK(signal(kBlockSignal, +[](int signo) {}) != SIG_ERR); + MaybeSave(); + TEST_PCHECK(sigprocmask(SIG_SETMASK, &blocked, nullptr) == 0); + MaybeSave(); + + // Enable tracing. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + + // This should be blocked. + RaiseSignal(kBlockSignal); + + // This should be suppressed by parent, who will change signal mask in the + // meantime, which means kBlockSignal should be delivered once this resumes. + RaiseSignal(SIGSTOP); + + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Get current signal mask. + sigset_t set; + EXPECT_THAT(ptrace(kPtraceGetSigMask, child_pid, kSizeofKernelSigset, &set), + SyscallSucceeds()); + EXPECT_THAT(blocked, EqualsSigset(set)); + + // Try to get current signal mask with bad size argument. + EXPECT_THAT(ptrace(kPtraceGetSigMask, child_pid, 0, nullptr), + SyscallFailsWithErrno(EINVAL)); + + // Try to set bad signal mask. + sigset_t* bad_addr = reinterpret_cast(-1); + EXPECT_THAT( + ptrace(kPtraceSetSigMask, child_pid, kSizeofKernelSigset, bad_addr), + SyscallFailsWithErrno(EFAULT)); + + // Set signal mask to empty set. + sigset_t set1; + sigemptyset(&set1); + EXPECT_THAT(ptrace(kPtraceSetSigMask, child_pid, kSizeofKernelSigset, &set1), + SyscallSucceeds()); + + // Suppress SIGSTOP and resume the child. It should re-enter + // signal-delivery-stop for kBlockSignal. + ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == kBlockSignal) + << " status " << status; + + ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + // Let's see that process exited normally. + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +TEST(PtraceTest, GetSiginfo_SetSiginfo_SignalInjection) { + constexpr int kOriginalSigno = SIGUSR1; + constexpr int kInjectedSigno = SIGUSR2; + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Override all signal handlers. + struct sigaction sa = {}; + sa.sa_handler = +[](int signo) { _exit(signo); }; + TEST_PCHECK(sigfillset(&sa.sa_mask) == 0); + for (int signo = 1; signo < 32; signo++) { + if (signo == SIGKILL || signo == SIGSTOP) { + continue; + } + TEST_PCHECK(sigaction(signo, &sa, nullptr) == 0); + } + for (int signo = SIGRTMIN; signo <= SIGRTMAX; signo++) { + TEST_PCHECK(sigaction(signo, &sa, nullptr) == 0); + } + + // Unblock all signals. + TEST_PCHECK(sigprocmask(SIG_UNBLOCK, &sa.sa_mask, nullptr) == 0); + MaybeSave(); + + // Send ourselves kOriginalSignal while ptraced and exit with the signal we + // actually receive via the signal handler, if any, or 0 if we don't receive + // a signal. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + RaiseSignal(kOriginalSigno); + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself kOriginalSigno and enter + // signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == kOriginalSigno) + << " status " << status; + + siginfo_t siginfo = {}; + ASSERT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), + SyscallSucceeds()); + EXPECT_EQ(kOriginalSigno, siginfo.si_signo); + EXPECT_EQ(SI_TKILL, siginfo.si_code); + + // Replace the signal with kInjectedSigno, and check that the child exits + // with kInjectedSigno, indicating that signal injection was successful. + siginfo.si_signo = kInjectedSigno; + ASSERT_THAT(ptrace(PTRACE_SETSIGINFO, child_pid, 0, &siginfo), + SyscallSucceeds()); + ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, kInjectedSigno), + SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == kInjectedSigno) + << " status " << status; +} + +TEST(PtraceTest, SIGKILLDoesNotCauseSignalDeliveryStop) { + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + RaiseSignal(SIGKILL); + TEST_CHECK_MSG(false, "Survived SIGKILL?"); + _exit(1); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Expect the child to die to SIGKILL without entering signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + << " status " << status; +} + +TEST(PtraceTest, PtraceKill) { + constexpr int kOriginalSigno = SIGUSR1; + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + + // PTRACE_KILL only works if tracee has entered signal-delivery-stop. + RaiseSignal(kOriginalSigno); + TEST_CHECK_MSG(false, "Failed to kill the process?"); + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself kOriginalSigno and enter + // signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == kOriginalSigno) + << " status " << status; + + ASSERT_THAT(ptrace(PTRACE_KILL, child_pid, 0, 0), SyscallSucceeds()); + + // Expect the child to die with SIGKILL. + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + << " status " << status; +} + +TEST(PtraceTest, GetRegSet) { + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Enable tracing. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + + // Use kill explicitly because we check the syscall argument register below. + kill(getpid(), SIGSTOP); + + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Get the general registers. + struct user_regs_struct regs; + struct iovec iov; + iov.iov_base = ®s; + iov.iov_len = sizeof(regs); + EXPECT_THAT(ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &iov), + SyscallSucceeds()); + + // Read exactly the full register set. + EXPECT_EQ(iov.iov_len, sizeof(regs)); + +#ifdef __x86_64__ + // Child called kill(2), with SIGSTOP as arg 2. + EXPECT_EQ(regs.rsi, SIGSTOP); +#endif + + // Suppress SIGSTOP and resume the child. + ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + // Let's see that process exited normally. + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +TEST(PtraceTest, AttachingConvertsGroupStopToPtraceStop) { + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + while (true) { + pause(); + } + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // SIGSTOP the child and wait for it to stop. + ASSERT_THAT(kill(child_pid, SIGSTOP), SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(child_pid, &status, WUNTRACED), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Attach to the child and expect it to re-enter a traced group-stop despite + // already being stopped. + ASSERT_THAT(ptrace(PTRACE_ATTACH, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Verify that the child is ptrace-stopped by checking that it can receive + // ptrace commands requiring a ptrace-stop. + EXPECT_THAT(ptrace(PTRACE_SETOPTIONS, child_pid, 0, 0), SyscallSucceeds()); + + // Group-stop is distinguished from signal-delivery-stop by PTRACE_GETSIGINFO + // failing with EINVAL. + siginfo_t siginfo = {}; + EXPECT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), + SyscallFailsWithErrno(EINVAL)); + + // Detach from the child and expect it to stay stopped without a notification. + ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, WUNTRACED | WNOHANG), + SyscallSucceedsWithValue(0)); + + // Sending it SIGCONT should cause it to leave its stop. + ASSERT_THAT(kill(child_pid, SIGCONT), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, WCONTINUED), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFCONTINUED(status)) << " status " << status; + + // Clean up the child. + ASSERT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + << " status " << status; +} + +// Fixture for tests parameterized by whether or not to use PTRACE_O_TRACEEXEC. +class PtraceExecveTest : public ::testing::TestWithParam { + protected: + bool TraceExec() const { return GetParam(); } +}; + +TEST_P(PtraceExecveTest, Execve_GetRegs_PeekUser_SIGKILL_TraceClone_TraceExit) { + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + RaiseSignal(SIGSTOP); + MaybeSave(); + + // Call execve in a non-leader thread. + ExecveArray const owned_child_argv = {"/proc/self/exe"}; + char* const* const child_argv = owned_child_argv.get(); + ScopedThread t([&] { + execve(child_argv[0], child_argv, /* envp = */ nullptr); + TEST_CHECK_MSG(false, "Survived execve? (thread)"); + }); + t.Join(); + TEST_CHECK_MSG(false, "Survived execve? (main)"); + _exit(1); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Enable PTRACE_O_TRACECLONE so we can get the ID of the child's non-leader + // thread, PTRACE_O_TRACEEXIT so we can observe the leader's death, and + // PTRACE_O_TRACEEXEC if required by the test. (The leader doesn't call + // execve, but options should be inherited across clone.) + long opts = PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT; + if (TraceExec()) { + opts |= PTRACE_O_TRACEEXEC; + } + ASSERT_THAT(ptrace(PTRACE_SETOPTIONS, child_pid, 0, opts), SyscallSucceeds()); + + // Suppress the SIGSTOP and wait for the child's leader thread to report + // PTRACE_EVENT_CLONE. Get the new thread's ID from the event. + ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_CLONE << 8), status >> 8); + unsigned long eventmsg; + ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, child_pid, 0, &eventmsg), + SyscallSucceeds()); + pid_t const nonleader_tid = eventmsg; + pid_t const leader_tid = child_pid; + + // The new thread should be ptraced and in signal-delivery-stop by SIGSTOP due + // to PTRACE_O_TRACECLONE. + // + // Before bf959931ddb88c4e4366e96dd22e68fa0db9527c "wait/ptrace: assume __WALL + // if the child is traced" (4.7) , waiting on it requires __WCLONE since, as a + // non-leader, its termination signal is 0. After, a standard wait is + // sufficient. + ASSERT_THAT(waitpid(nonleader_tid, &status, __WCLONE), + SyscallSucceedsWithValue(nonleader_tid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Resume both child threads. + for (pid_t const tid : {leader_tid, nonleader_tid}) { + ASSERT_THAT(ptrace(PTRACE_CONT, tid, 0, 0), SyscallSucceeds()); + } + + // The non-leader child thread should call execve, causing the leader thread + // to enter PTRACE_EVENT_EXIT with an apparent exit code of 0. At this point, + // the leader has not yet exited, so the non-leader should be blocked in + // execve. + ASSERT_THAT(waitpid(leader_tid, &status, 0), + SyscallSucceedsWithValue(leader_tid)); + EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_EXIT << 8), status >> 8); + ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, leader_tid, 0, &eventmsg), + SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(eventmsg) && WEXITSTATUS(eventmsg) == 0) + << " eventmsg " << eventmsg; + EXPECT_THAT(waitpid(nonleader_tid, &status, __WCLONE | WNOHANG), + SyscallSucceedsWithValue(0)); + + // Allow the leader to continue exiting. This should allow the non-leader to + // complete its execve, causing the original leader to be reaped without + // further notice and the non-leader to steal its ID. + ASSERT_THAT(ptrace(PTRACE_CONT, leader_tid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(leader_tid, &status, 0), + SyscallSucceedsWithValue(leader_tid)); + if (TraceExec()) { + // If PTRACE_O_TRACEEXEC was enabled, the execing thread should be in + // PTRACE_EVENT_EXEC-stop, with the event message set to its old thread ID. + EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_EXEC << 8), status >> 8); + ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, leader_tid, 0, &eventmsg), + SyscallSucceeds()); + EXPECT_EQ(nonleader_tid, eventmsg); + } else { + // Otherwise, the execing thread should have received SIGTRAP and should now + // be in signal-delivery-stop. + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) + << " status " << status; + } + +#ifdef __x86_64__ + { + // CS should be 0x33, indicating an 64-bit binary. + constexpr uint64_t kAMD64UserCS = 0x33; + EXPECT_THAT(ptrace(PTRACE_PEEKUSER, leader_tid, + offsetof(struct user_regs_struct, cs), 0), + SyscallSucceedsWithValue(kAMD64UserCS)); + struct user_regs_struct regs = {}; + ASSERT_THAT(ptrace(PTRACE_GETREGS, leader_tid, 0, ®s), + SyscallSucceeds()); + EXPECT_EQ(kAMD64UserCS, regs.cs); + } +#endif // defined(__x86_64__) + + // PTRACE_O_TRACEEXIT should have been inherited across execve. Send SIGKILL, + // which should end the PTRACE_EVENT_EXEC-stop or signal-delivery-stop and + // leave the child in PTRACE_EVENT_EXIT-stop. + ASSERT_THAT(kill(leader_tid, SIGKILL), SyscallSucceeds()); + ASSERT_THAT(waitpid(leader_tid, &status, 0), + SyscallSucceedsWithValue(leader_tid)); + EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_EXIT << 8), status >> 8); + ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, leader_tid, 0, &eventmsg), + SyscallSucceeds()); + EXPECT_TRUE(WIFSIGNALED(eventmsg) && WTERMSIG(eventmsg) == SIGKILL) + << " eventmsg " << eventmsg; + + // End the PTRACE_EVENT_EXIT stop, allowing the child to exit. + ASSERT_THAT(ptrace(PTRACE_CONT, leader_tid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(leader_tid, &status, 0), + SyscallSucceedsWithValue(leader_tid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + << " status " << status; +} + +INSTANTIATE_TEST_CASE_P(TraceExec, PtraceExecveTest, ::testing::Bool()); + +// This test has expectations on when syscall-enter/exit-stops occur that are +// violated if saving occurs, since saving interrupts all syscalls, causing +// premature syscall-exit. +TEST(PtraceTest, + ExitWhenParentIsNotTracer_Syscall_TraceVfork_TraceVforkDone_NoRandomSave) { + constexpr int kExitTraceeExitCode = 99; + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Block SIGCHLD so it doesn't interrupt wait4. + sigset_t mask; + TEST_PCHECK(sigemptyset(&mask) == 0); + TEST_PCHECK(sigaddset(&mask, SIGCHLD) == 0); + TEST_PCHECK(sigprocmask(SIG_SETMASK, &mask, nullptr) == 0); + MaybeSave(); + + // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + RaiseSignal(SIGSTOP); + MaybeSave(); + + // Spawn a vfork child that exits immediately, and reap it. Don't save + // after vfork since the parent expects to see wait4 as the next syscall. + pid_t const pid = vfork(); + if (pid == 0) { + _exit(kExitTraceeExitCode); + } + TEST_PCHECK_MSG(pid > 0, "vfork failed"); + + int status; + TEST_PCHECK(wait4(pid, &status, 0, nullptr) > 0); + MaybeSave(); + TEST_CHECK(WIFEXITED(status) && WEXITSTATUS(status) == kExitTraceeExitCode); + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. + int status; + ASSERT_THAT(child_pid, SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Enable PTRACE_O_TRACEVFORK so we can get the ID of the grandchild, + // PTRACE_O_TRACEVFORKDONE so we can observe PTRACE_EVENT_VFORK_DONE, and + // PTRACE_O_TRACESYSGOOD so syscall-enter/exit-stops are unambiguously + // indicated by a stop signal of SIGTRAP|0x80 rather than just SIGTRAP. + ASSERT_THAT(ptrace(PTRACE_SETOPTIONS, child_pid, 0, + PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | + PTRACE_O_TRACESYSGOOD), + SyscallSucceeds()); + + // Suppress the SIGSTOP and wait for the child to report PTRACE_EVENT_VFORK. + // Get the new process' ID from the event. + ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_VFORK << 8), status >> 8); + unsigned long eventmsg; + ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, child_pid, 0, &eventmsg), + SyscallSucceeds()); + pid_t const grandchild_pid = eventmsg; + + // The grandchild should be traced by us and in signal-delivery-stop by + // SIGSTOP due to PTRACE_O_TRACEVFORK. This allows us to wait on it even + // though we're not its parent. + ASSERT_THAT(waitpid(grandchild_pid, &status, 0), + SyscallSucceedsWithValue(grandchild_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Resume the child with PTRACE_SYSCALL. Since the grandchild is still in + // signal-delivery-stop, the child should remain in vfork() waiting for the + // grandchild to exec or exit. + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); + absl::SleepFor(absl::Seconds(1)); + ASSERT_THAT(waitpid(child_pid, &status, WNOHANG), + SyscallSucceedsWithValue(0)); + + // Suppress the grandchild's SIGSTOP and wait for the grandchild to exit. Pass + // WNOWAIT to waitid() so that we don't acknowledge the grandchild's exit yet. + ASSERT_THAT(ptrace(PTRACE_CONT, grandchild_pid, 0, 0), SyscallSucceeds()); + siginfo_t siginfo = {}; + ASSERT_THAT(waitid(P_PID, grandchild_pid, &siginfo, WEXITED | WNOWAIT), + SyscallSucceeds()); + EXPECT_EQ(SIGCHLD, siginfo.si_signo); + EXPECT_EQ(CLD_EXITED, siginfo.si_code); + EXPECT_EQ(kExitTraceeExitCode, siginfo.si_status); + EXPECT_EQ(grandchild_pid, siginfo.si_pid); + EXPECT_EQ(getuid(), siginfo.si_uid); + + // The child should now be in PTRACE_EVENT_VFORK_DONE stop. The event + // message should still be the grandchild's PID. + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_VFORK_DONE << 8), status >> 8); + ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, child_pid, 0, &eventmsg), + SyscallSucceeds()); + EXPECT_EQ(grandchild_pid, eventmsg); + + // Resume the child with PTRACE_SYSCALL again and expect it to enter + // syscall-exit-stop for vfork() or clone(), either of which should return the + // grandchild's PID from the syscall. Aside from PTRACE_O_TRACESYSGOOD, + // syscall-stops are distinguished from signal-delivery-stop by + // PTRACE_GETSIGINFO returning a siginfo for which si_code == SIGTRAP or + // SIGTRAP|0x80. + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) + << " status " << status; + ASSERT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), + SyscallSucceeds()); + EXPECT_TRUE(siginfo.si_code == SIGTRAP || siginfo.si_code == (SIGTRAP | 0x80)) + << "si_code = " << siginfo.si_code; +#ifdef __x86_64__ + { + struct user_regs_struct regs = {}; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); + EXPECT_TRUE(regs.orig_rax == SYS_vfork || regs.orig_rax == SYS_clone) + << "orig_rax = " << regs.orig_rax; + EXPECT_EQ(grandchild_pid, regs.rax); + } +#endif // defined(__x86_64__) + + // After this point, the child will be making wait4 syscalls that will be + // interrupted by saving, so saving is not permitted. Note that this is + // explicitly released below once the grandchild exits. + DisableSave ds; + + // Resume the child with PTRACE_SYSCALL again and expect it to enter + // syscall-enter-stop for wait4(). + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) + << " status " << status; + ASSERT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), + SyscallSucceeds()); + EXPECT_TRUE(siginfo.si_code == SIGTRAP || siginfo.si_code == (SIGTRAP | 0x80)) + << "si_code = " << siginfo.si_code; +#ifdef __x86_64__ + { + EXPECT_THAT(ptrace(PTRACE_PEEKUSER, child_pid, + offsetof(struct user_regs_struct, orig_rax), 0), + SyscallSucceedsWithValue(SYS_wait4)); + } +#endif // defined(__x86_64__) + + // Resume the child with PTRACE_SYSCALL again. Since the grandchild is + // waiting for the tracer (us) to acknowledge its exit first, wait4 should + // block. + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); + absl::SleepFor(absl::Seconds(1)); + ASSERT_THAT(waitpid(child_pid, &status, WNOHANG), + SyscallSucceedsWithValue(0)); + + // Acknowledge the grandchild's exit. + ASSERT_THAT(waitpid(grandchild_pid, &status, 0), + SyscallSucceedsWithValue(grandchild_pid)); + ds.reset(); + + // Now the child should enter syscall-exit-stop for wait4, returning with the + // grandchild's PID. + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) + << " status " << status; +#ifdef __x86_64__ + { + struct user_regs_struct regs = {}; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); + EXPECT_EQ(SYS_wait4, regs.orig_rax); + EXPECT_EQ(grandchild_pid, regs.rax); + } +#endif // defined(__x86_64__) + + // Detach from the child and wait for it to exit. + ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +// These tests requires knowledge of architecture-specific syscall convention. +#ifdef __x86_64__ +TEST(PtraceTest, Sysemu_PokeUser) { + constexpr int kSysemuHelperFirstExitCode = 126; + constexpr uint64_t kSysemuInjectedExitGroupReturn = 42; + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + RaiseSignal(SIGSTOP); + + // Try to exit_group, expecting the tracer to skip the syscall and set its + // own return value. + int const rv = syscall(SYS_exit_group, kSysemuHelperFirstExitCode); + TEST_PCHECK_MSG(rv == kSysemuInjectedExitGroupReturn, + "exit_group returned incorrect value"); + + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // Suppress the SIGSTOP and wait for the child to enter syscall-enter-stop + // for its first exit_group syscall. glibc doesn't necessarily define + // PTRACE_SYSEMU. + constexpr auto kPtraceSysemu = static_cast<__ptrace_request>(31); + ASSERT_THAT(ptrace(kPtraceSysemu, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) + << " status " << status; + + struct user_regs_struct regs = {}; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); + EXPECT_EQ(SYS_exit_group, regs.orig_rax); + EXPECT_EQ(-ENOSYS, regs.rax); + EXPECT_EQ(kSysemuHelperFirstExitCode, regs.rdi); + + // Replace the exit_group return value, then resume the child, which should + // automatically skip the syscall. + ASSERT_THAT( + ptrace(PTRACE_POKEUSER, child_pid, offsetof(struct user_regs_struct, rax), + kSysemuInjectedExitGroupReturn), + SyscallSucceeds()); + ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); + + // The child should validate the injected return value and then exit normally. + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} + +// This test also cares about syscall-exit-stop. +TEST(PtraceTest, ERESTART_NoRandomSave) { + constexpr int kSigno = SIGUSR1; + + pid_t const child_pid = fork(); + if (child_pid == 0) { + // In child process. + + // Ignore, but unblock, kSigno. + struct sigaction sa = {}; + sa.sa_handler = SIG_IGN; + TEST_PCHECK(sigfillset(&sa.sa_mask) == 0); + TEST_PCHECK(sigaction(kSigno, &sa, nullptr) == 0); + MaybeSave(); + TEST_PCHECK(sigprocmask(SIG_UNBLOCK, &sa.sa_mask, nullptr) == 0); + MaybeSave(); + + // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + RaiseSignal(SIGSTOP); + + // Invoke the pause syscall, which normally should not return until we + // receive a signal that "either terminates the process or causes the + // invocation of a signal-catching function". + pause(); + + _exit(0); + } + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + << " status " << status; + + // After this point, the child's pause syscall will be interrupted by saving, + // so saving is not permitted. Note that this is explicitly released below + // once the child is stopped. + DisableSave ds; + + // Suppress the SIGSTOP and wait for the child to enter syscall-enter-stop for + // its pause syscall. + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) + << " status " << status; + + struct user_regs_struct regs = {}; + ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); + EXPECT_EQ(SYS_pause, regs.orig_rax); + EXPECT_EQ(-ENOSYS, regs.rax); + + // Resume the child with PTRACE_SYSCALL and expect it to block in the pause + // syscall. + ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); + absl::SleepFor(absl::Seconds(1)); + ASSERT_THAT(waitpid(child_pid, &status, WNOHANG), + SyscallSucceedsWithValue(0)); + + // Send the child kSigno, causing it to return ERESTARTNOHAND and enter + // syscall-exit-stop from the pause syscall. + constexpr int ERESTARTNOHAND = 514; + ASSERT_THAT(kill(child_pid, kSigno), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) + << " status " << status; + ds.reset(); + + ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); + EXPECT_EQ(SYS_pause, regs.orig_rax); + EXPECT_EQ(-ERESTARTNOHAND, regs.rax); + + // Replace the return value from pause with 0, causing pause to not be + // restarted despite kSigno being ignored. + ASSERT_THAT(ptrace(PTRACE_POKEUSER, child_pid, + offsetof(struct user_regs_struct, rax), 0), + SyscallSucceeds()); + + // Detach from the child and wait for it to exit. + ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; +} +#endif // defined(__x86_64__) + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc new file mode 100644 index 000000000..253aa26ba --- /dev/null +++ b/test/syscalls/linux/pty.cc @@ -0,0 +1,1230 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/macros.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +using ::testing::AnyOf; +using ::testing::Contains; +using ::testing::Eq; +using ::testing::Not; + +// Tests Unix98 pseudoterminals. +// +// These tests assume that /dev/ptmx exists and is associated with a devpts +// filesystem mounted at /dev/pts/. While a Linux distribution could +// theoretically place those anywhere, glibc expects those locations, so they +// are effectively fixed. + +// Minor device number for an unopened ptmx file. +constexpr int kPtmxMinor = 2; + +// The timeout when polling for data from a pty. When data is written to one end +// of a pty, Linux asynchronously makes it available to the other end, so we +// have to wait. +constexpr absl::Duration kTimeout = absl::Seconds(20); + +// The maximum line size in bytes returned per read from a pty file. +constexpr int kMaxLineSize = 4096; + +// glibc defines its own, different, version of struct termios. We care about +// what the kernel does, not glibc. +#define KERNEL_NCCS 19 +struct kernel_termios { + tcflag_t c_iflag; + tcflag_t c_oflag; + tcflag_t c_cflag; + tcflag_t c_lflag; + cc_t c_line; + cc_t c_cc[KERNEL_NCCS]; +}; + +bool operator==(struct kernel_termios const& a, + struct kernel_termios const& b) { + return memcmp(&a, &b, sizeof(a)) == 0; +} + +// Returns the termios-style control character for the passed character. +// +// e.g., for Ctrl-C, i.e., ^C, call ControlCharacter('C'). +// +// Standard control characters are ASCII bytes 0 through 31. +constexpr char ControlCharacter(char c) { + // A is 1, B is 2, etc. + return c - 'A' + 1; +} + +// Returns the printable character the given control character represents. +constexpr char FromControlCharacter(char c) { return c + 'A' - 1; } + +// Returns true if c is a control character. +// +// Standard control characters are ASCII bytes 0 through 31. +constexpr bool IsControlCharacter(char c) { return c <= 31; } + +struct Field { + const char* name; + uint64_t mask; + uint64_t value; +}; + +// ParseFields returns a std::string representation of value, using the names in +// fields. +std::string ParseFields(const Field* fields, size_t len, uint64_t value) { + bool first = true; + std::string s; + for (size_t i = 0; i < len; i++) { + const Field f = fields[i]; + if ((value & f.mask) == f.value) { + if (!first) { + s += "|"; + } + s += f.name; + first = false; + value &= ~f.mask; + } + } + + if (value) { + if (!first) { + s += "|"; + } + absl::StrAppend(&s, value); + } + + return s; +} + +const Field kIflagFields[] = { + {"IGNBRK", IGNBRK, IGNBRK}, {"BRKINT", BRKINT, BRKINT}, + {"IGNPAR", IGNPAR, IGNPAR}, {"PARMRK", PARMRK, PARMRK}, + {"INPCK", INPCK, INPCK}, {"ISTRIP", ISTRIP, ISTRIP}, + {"INLCR", INLCR, INLCR}, {"IGNCR", IGNCR, IGNCR}, + {"ICRNL", ICRNL, ICRNL}, {"IUCLC", IUCLC, IUCLC}, + {"IXON", IXON, IXON}, {"IXANY", IXANY, IXANY}, + {"IXOFF", IXOFF, IXOFF}, {"IMAXBEL", IMAXBEL, IMAXBEL}, + {"IUTF8", IUTF8, IUTF8}, +}; + +const Field kOflagFields[] = { + {"OPOST", OPOST, OPOST}, {"OLCUC", OLCUC, OLCUC}, + {"ONLCR", ONLCR, ONLCR}, {"OCRNL", OCRNL, OCRNL}, + {"ONOCR", ONOCR, ONOCR}, {"ONLRET", ONLRET, ONLRET}, + {"OFILL", OFILL, OFILL}, {"OFDEL", OFDEL, OFDEL}, + {"NL0", NLDLY, NL0}, {"NL1", NLDLY, NL1}, + {"CR0", CRDLY, CR0}, {"CR1", CRDLY, CR1}, + {"CR2", CRDLY, CR2}, {"CR3", CRDLY, CR3}, + {"TAB0", TABDLY, TAB0}, {"TAB1", TABDLY, TAB1}, + {"TAB2", TABDLY, TAB2}, {"TAB3", TABDLY, TAB3}, + {"BS0", BSDLY, BS0}, {"BS1", BSDLY, BS1}, + {"FF0", FFDLY, FF0}, {"FF1", FFDLY, FF1}, + {"VT0", VTDLY, VT0}, {"VT1", VTDLY, VT1}, + {"XTABS", XTABS, XTABS}, +}; + +#ifndef IBSHIFT +// Shift from CBAUD to CIBAUD. +#define IBSHIFT 16 +#endif + +const Field kCflagFields[] = { + {"B0", CBAUD, B0}, + {"B50", CBAUD, B50}, + {"B75", CBAUD, B75}, + {"B110", CBAUD, B110}, + {"B134", CBAUD, B134}, + {"B150", CBAUD, B150}, + {"B200", CBAUD, B200}, + {"B300", CBAUD, B300}, + {"B600", CBAUD, B600}, + {"B1200", CBAUD, B1200}, + {"B1800", CBAUD, B1800}, + {"B2400", CBAUD, B2400}, + {"B4800", CBAUD, B4800}, + {"B9600", CBAUD, B9600}, + {"B19200", CBAUD, B19200}, + {"B38400", CBAUD, B38400}, + {"CS5", CSIZE, CS5}, + {"CS6", CSIZE, CS6}, + {"CS7", CSIZE, CS7}, + {"CS8", CSIZE, CS8}, + {"CSTOPB", CSTOPB, CSTOPB}, + {"CREAD", CREAD, CREAD}, + {"PARENB", PARENB, PARENB}, + {"PARODD", PARODD, PARODD}, + {"HUPCL", HUPCL, HUPCL}, + {"CLOCAL", CLOCAL, CLOCAL}, + {"B57600", CBAUD, B57600}, + {"B115200", CBAUD, B115200}, + {"B230400", CBAUD, B230400}, + {"B460800", CBAUD, B460800}, + {"B500000", CBAUD, B500000}, + {"B576000", CBAUD, B576000}, + {"B921600", CBAUD, B921600}, + {"B1000000", CBAUD, B1000000}, + {"B1152000", CBAUD, B1152000}, + {"B1500000", CBAUD, B1500000}, + {"B2000000", CBAUD, B2000000}, + {"B2500000", CBAUD, B2500000}, + {"B3000000", CBAUD, B3000000}, + {"B3500000", CBAUD, B3500000}, + {"B4000000", CBAUD, B4000000}, + {"CMSPAR", CMSPAR, CMSPAR}, + {"CRTSCTS", CRTSCTS, CRTSCTS}, + {"IB0", CIBAUD, B0 << IBSHIFT}, + {"IB50", CIBAUD, B50 << IBSHIFT}, + {"IB75", CIBAUD, B75 << IBSHIFT}, + {"IB110", CIBAUD, B110 << IBSHIFT}, + {"IB134", CIBAUD, B134 << IBSHIFT}, + {"IB150", CIBAUD, B150 << IBSHIFT}, + {"IB200", CIBAUD, B200 << IBSHIFT}, + {"IB300", CIBAUD, B300 << IBSHIFT}, + {"IB600", CIBAUD, B600 << IBSHIFT}, + {"IB1200", CIBAUD, B1200 << IBSHIFT}, + {"IB1800", CIBAUD, B1800 << IBSHIFT}, + {"IB2400", CIBAUD, B2400 << IBSHIFT}, + {"IB4800", CIBAUD, B4800 << IBSHIFT}, + {"IB9600", CIBAUD, B9600 << IBSHIFT}, + {"IB19200", CIBAUD, B19200 << IBSHIFT}, + {"IB38400", CIBAUD, B38400 << IBSHIFT}, + {"IB57600", CIBAUD, B57600 << IBSHIFT}, + {"IB115200", CIBAUD, B115200 << IBSHIFT}, + {"IB230400", CIBAUD, B230400 << IBSHIFT}, + {"IB460800", CIBAUD, B460800 << IBSHIFT}, + {"IB500000", CIBAUD, B500000 << IBSHIFT}, + {"IB576000", CIBAUD, B576000 << IBSHIFT}, + {"IB921600", CIBAUD, B921600 << IBSHIFT}, + {"IB1000000", CIBAUD, B1000000 << IBSHIFT}, + {"IB1152000", CIBAUD, B1152000 << IBSHIFT}, + {"IB1500000", CIBAUD, B1500000 << IBSHIFT}, + {"IB2000000", CIBAUD, B2000000 << IBSHIFT}, + {"IB2500000", CIBAUD, B2500000 << IBSHIFT}, + {"IB3000000", CIBAUD, B3000000 << IBSHIFT}, + {"IB3500000", CIBAUD, B3500000 << IBSHIFT}, + {"IB4000000", CIBAUD, B4000000 << IBSHIFT}, +}; + +const Field kLflagFields[] = { + {"ISIG", ISIG, ISIG}, {"ICANON", ICANON, ICANON}, + {"XCASE", XCASE, XCASE}, {"ECHO", ECHO, ECHO}, + {"ECHOE", ECHOE, ECHOE}, {"ECHOK", ECHOK, ECHOK}, + {"ECHONL", ECHONL, ECHONL}, {"NOFLSH", NOFLSH, NOFLSH}, + {"TOSTOP", TOSTOP, TOSTOP}, {"ECHOCTL", ECHOCTL, ECHOCTL}, + {"ECHOPRT", ECHOPRT, ECHOPRT}, {"ECHOKE", ECHOKE, ECHOKE}, + {"FLUSHO", FLUSHO, FLUSHO}, {"PENDIN", PENDIN, PENDIN}, + {"IEXTEN", IEXTEN, IEXTEN}, {"EXTPROC", EXTPROC, EXTPROC}, +}; + +std::string FormatCC(char c) { + if (isgraph(c)) { + return std::string(1, c); + } else if (c == ' ') { + return " "; + } else if (c == '\t') { + return "\\t"; + } else if (c == '\r') { + return "\\r"; + } else if (c == '\n') { + return "\\n"; + } else if (c == '\0') { + return "\\0"; + } else if (IsControlCharacter(c)) { + return absl::StrCat("^", std::string(1, FromControlCharacter(c))); + } + return absl::StrCat("\\x", absl::Hex(c)); +} + +std::ostream& operator<<(std::ostream& os, struct kernel_termios const& a) { + os << "{ c_iflag = " + << ParseFields(kIflagFields, ABSL_ARRAYSIZE(kIflagFields), a.c_iflag); + os << ", c_oflag = " + << ParseFields(kOflagFields, ABSL_ARRAYSIZE(kOflagFields), a.c_oflag); + os << ", c_cflag = " + << ParseFields(kCflagFields, ABSL_ARRAYSIZE(kCflagFields), a.c_cflag); + os << ", c_lflag = " + << ParseFields(kLflagFields, ABSL_ARRAYSIZE(kLflagFields), a.c_lflag); + os << ", c_line = " << a.c_line; + os << ", c_cc = { [VINTR] = '" << FormatCC(a.c_cc[VINTR]); + os << "', [VQUIT] = '" << FormatCC(a.c_cc[VQUIT]); + os << "', [VERASE] = '" << FormatCC(a.c_cc[VERASE]); + os << "', [VKILL] = '" << FormatCC(a.c_cc[VKILL]); + os << "', [VEOF] = '" << FormatCC(a.c_cc[VEOF]); + os << "', [VTIME] = '" << static_cast(a.c_cc[VTIME]); + os << "', [VMIN] = " << static_cast(a.c_cc[VMIN]); + os << ", [VSWTC] = '" << FormatCC(a.c_cc[VSWTC]); + os << "', [VSTART] = '" << FormatCC(a.c_cc[VSTART]); + os << "', [VSTOP] = '" << FormatCC(a.c_cc[VSTOP]); + os << "', [VSUSP] = '" << FormatCC(a.c_cc[VSUSP]); + os << "', [VEOL] = '" << FormatCC(a.c_cc[VEOL]); + os << "', [VREPRINT] = '" << FormatCC(a.c_cc[VREPRINT]); + os << "', [VDISCARD] = '" << FormatCC(a.c_cc[VDISCARD]); + os << "', [VWERASE] = '" << FormatCC(a.c_cc[VWERASE]); + os << "', [VLNEXT] = '" << FormatCC(a.c_cc[VLNEXT]); + os << "', [VEOL2] = '" << FormatCC(a.c_cc[VEOL2]); + os << "'}"; + return os; +} + +// Return the default termios settings for a new terminal. +struct kernel_termios DefaultTermios() { + struct kernel_termios t = {}; + t.c_iflag = IXON | ICRNL; + t.c_oflag = OPOST | ONLCR; + t.c_cflag = B38400 | CSIZE | CS8 | CREAD; + t.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; + t.c_line = 0; + t.c_cc[VINTR] = ControlCharacter('C'); + t.c_cc[VQUIT] = ControlCharacter('\\'); + t.c_cc[VERASE] = '\x7f'; + t.c_cc[VKILL] = ControlCharacter('U'); + t.c_cc[VEOF] = ControlCharacter('D'); + t.c_cc[VTIME] = '\0'; + t.c_cc[VMIN] = 1; + t.c_cc[VSWTC] = '\0'; + t.c_cc[VSTART] = ControlCharacter('Q'); + t.c_cc[VSTOP] = ControlCharacter('S'); + t.c_cc[VSUSP] = ControlCharacter('Z'); + t.c_cc[VEOL] = '\0'; + t.c_cc[VREPRINT] = ControlCharacter('R'); + t.c_cc[VDISCARD] = ControlCharacter('O'); + t.c_cc[VWERASE] = ControlCharacter('W'); + t.c_cc[VLNEXT] = ControlCharacter('V'); + t.c_cc[VEOL2] = '\0'; + return t; +} + +// PollAndReadFd tries to read count bytes from buf within timeout. +// +// Returns a partial read if some bytes were read. +// +// fd must be non-blocking. +PosixErrorOr PollAndReadFd(int fd, void* buf, size_t count, + absl::Duration timeout) { + absl::Time end = absl::Now() + timeout; + + size_t completed = 0; + absl::Duration remaining; + while ((remaining = end - absl::Now()) > absl::ZeroDuration()) { + struct pollfd pfd = {fd, POLLIN, 0}; + int ret = RetryEINTR(poll)(&pfd, 1, absl::ToInt64Milliseconds(remaining)); + if (ret < 0) { + return PosixError(errno, "poll failed"); + } else if (ret == 0) { + // Timed out. + continue; + } else if (ret != 1) { + return PosixError(EINVAL, absl::StrCat("Bad poll ret ", ret)); + } + + ssize_t n = + ReadFd(fd, static_cast(buf) + completed, count - completed); + if (n < 0) { + return PosixError(errno, "read failed"); + } + completed += n; + if (completed >= count) { + return completed; + } + } + + if (completed) { + return completed; + } + return PosixError(ETIMEDOUT, "Poll timed out"); +} + +// Opens the slave end of the passed master as R/W and nonblocking. +PosixErrorOr OpenSlave(const FileDescriptor& master) { + // Get pty index. + int n; + int ret = ioctl(master.get(), TIOCGPTN, &n); + if (ret < 0) { + return PosixError(errno, "ioctl(TIOCGPTN) failed"); + } + + // Unlock pts. + int unlock = 0; + ret = ioctl(master.get(), TIOCSPTLCK, &unlock); + if (ret < 0) { + return PosixError(errno, "ioctl(TIOSPTLCK) failed"); + } + + return Open(absl::StrCat("/dev/pts/", n), O_RDWR | O_NONBLOCK); +} + +TEST(BasicPtyTest, StatUnopenedMaster) { + struct stat s; + ASSERT_THAT(stat("/dev/ptmx", &s), SyscallSucceeds()); + + EXPECT_EQ(s.st_rdev, makedev(TTYAUX_MAJOR, kPtmxMinor)); + EXPECT_EQ(s.st_size, 0); + EXPECT_EQ(s.st_blocks, 0); + + // ptmx attached to a specific devpts mount uses block size 1024. See + // fs/devpts/inode.c:devpts_fill_super. + // + // The global ptmx device uses the block size of the filesystem it is created + // on (which is usually 4096 for disk filesystems). + EXPECT_THAT(s.st_blksize, AnyOf(Eq(1024), Eq(4096))); +} + +// Waits for count bytes to be readable from fd. Unlike poll, which can return +// before all data is moved into a pty's read buffer, this function waits for +// all count bytes to become readable. +PosixErrorOr WaitUntilReceived(int fd, int count) { + int buffered = -1; + absl::Duration remaining; + absl::Time end = absl::Now() + kTimeout; + while ((remaining = end - absl::Now()) > absl::ZeroDuration()) { + if (ioctl(fd, FIONREAD, &buffered) < 0) { + return PosixError(errno, "failed FIONREAD ioctl"); + } + if (buffered >= count) { + return buffered; + } + absl::SleepFor(absl::Milliseconds(500)); + } + return PosixError( + ETIMEDOUT, + absl::StrFormat( + "FIONREAD timed out, receiving only %d of %d expected bytes", + buffered, count)); +} + +// Verifies that there is nothing left to read from fd. +void ExpectFinished(const FileDescriptor& fd) { + // Nothing more to read. + char c; + EXPECT_THAT(ReadFd(fd.get(), &c, 1), SyscallFailsWithErrno(EAGAIN)); +} + +// Verifies that we can read expected bytes from fd into buf. +void ExpectReadable(const FileDescriptor& fd, int expected, char* buf) { + size_t n = ASSERT_NO_ERRNO_AND_VALUE( + PollAndReadFd(fd.get(), buf, expected, kTimeout)); + EXPECT_EQ(expected, n); +} + +TEST(BasicPtyTest, OpenMasterSlave) { + FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); + FileDescriptor slave = ASSERT_NO_ERRNO_AND_VALUE(OpenSlave(master)); +} + +// The slave entry in /dev/pts/ disappears when the master is closed, even if +// the slave is still open. +TEST(BasicPtyTest, SlaveEntryGoneAfterMasterClose) { + FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); + FileDescriptor slave = ASSERT_NO_ERRNO_AND_VALUE(OpenSlave(master)); + + // Get pty index. + int index = -1; + ASSERT_THAT(ioctl(master.get(), TIOCGPTN, &index), SyscallSucceeds()); + + std::string path = absl::StrCat("/dev/pts/", index); + + struct stat st; + EXPECT_THAT(stat(path.c_str(), &st), SyscallSucceeds()); + + master.reset(); + + EXPECT_THAT(stat(path.c_str(), &st), SyscallFailsWithErrno(ENOENT)); +} + +TEST(BasicPtyTest, Getdents) { + FileDescriptor master1 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); + int index1 = -1; + ASSERT_THAT(ioctl(master1.get(), TIOCGPTN, &index1), SyscallSucceeds()); + FileDescriptor slave1 = ASSERT_NO_ERRNO_AND_VALUE(OpenSlave(master1)); + + FileDescriptor master2 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); + int index2 = -1; + ASSERT_THAT(ioctl(master2.get(), TIOCGPTN, &index2), SyscallSucceeds()); + FileDescriptor slave2 = ASSERT_NO_ERRNO_AND_VALUE(OpenSlave(master2)); + + // The directory contains ptmx, index1, and index2. (Plus any additional PTYs + // unrelated to this test.) + + std::vector contents = + ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev/pts/", true)); + EXPECT_THAT(contents, Contains(absl::StrCat(index1))); + EXPECT_THAT(contents, Contains(absl::StrCat(index2))); + + master2.reset(); + + // The directory contains ptmx and index1, but not index2 since the master is + // closed. (Plus any additional PTYs unrelated to this test.) + + contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev/pts/", true)); + EXPECT_THAT(contents, Contains(absl::StrCat(index1))); + EXPECT_THAT(contents, Not(Contains(absl::StrCat(index2)))); + + // N.B. devpts supports legacy "single-instance" mode and new "multi-instance" + // mode. In legacy mode, devpts does not contain a "ptmx" device (the distro + // must use mknod to create it somewhere, presumably /dev/ptmx). + // Multi-instance mode does include a "ptmx" device tied to that mount. + // + // We don't check for the presence or absence of "ptmx", as distros vary in + // their usage of the two modes. +} + +class PtyTest : public ::testing::Test { + protected: + void SetUp() override { + master_ = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK)); + slave_ = ASSERT_NO_ERRNO_AND_VALUE(OpenSlave(master_)); + } + + void DisableCanonical() { + struct kernel_termios t = {}; + EXPECT_THAT(ioctl(slave_.get(), TCGETS, &t), SyscallSucceeds()); + t.c_lflag &= ~ICANON; + EXPECT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + } + + void EnableCanonical() { + struct kernel_termios t = {}; + EXPECT_THAT(ioctl(slave_.get(), TCGETS, &t), SyscallSucceeds()); + t.c_lflag |= ICANON; + EXPECT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + } + + // Master and slave ends of the PTY. Non-blocking. + FileDescriptor master_; + FileDescriptor slave_; +}; + +// Master to slave sanity test. +TEST_F(PtyTest, WriteMasterToSlave) { + // N.B. by default, the slave reads nothing until the master writes a newline. + constexpr char kBuf[] = "hello\n"; + + EXPECT_THAT(WriteFd(master_.get(), kBuf, sizeof(kBuf) - 1), + SyscallSucceedsWithValue(sizeof(kBuf) - 1)); + + // Linux moves data from the master to the slave via async work scheduled via + // tty_flip_buffer_push. Since it is asynchronous, the data may not be + // available for reading immediately. Instead we must poll and assert that it + // becomes available "soon". + + char buf[sizeof(kBuf)] = {}; + ExpectReadable(slave_, sizeof(buf) - 1, buf); + + EXPECT_EQ(memcmp(buf, kBuf, sizeof(kBuf)), 0); +} + +// Slave to master sanity test. +TEST_F(PtyTest, WriteSlaveToMaster) { + // N.B. by default, the master reads nothing until the slave writes a newline, + // and the master gets a carriage return. + constexpr char kInput[] = "hello\n"; + constexpr char kExpected[] = "hello\r\n"; + + EXPECT_THAT(WriteFd(slave_.get(), kInput, sizeof(kInput) - 1), + SyscallSucceedsWithValue(sizeof(kInput) - 1)); + + // Linux moves data from the master to the slave via async work scheduled via + // tty_flip_buffer_push. Since it is asynchronous, the data may not be + // available for reading immediately. Instead we must poll and assert that it + // becomes available "soon". + + char buf[sizeof(kExpected)] = {}; + ExpectReadable(master_, sizeof(buf) - 1, buf); + + EXPECT_EQ(memcmp(buf, kExpected, sizeof(kExpected)), 0); +} + +// Both the master and slave report the standard default termios settings. +// +// Note that TCGETS on the master actually redirects to the slave (see comment +// on MasterTermiosUnchangable). +TEST_F(PtyTest, DefaultTermios) { + struct kernel_termios t = {}; + EXPECT_THAT(ioctl(slave_.get(), TCGETS, &t), SyscallSucceeds()); + EXPECT_EQ(t, DefaultTermios()); + + EXPECT_THAT(ioctl(master_.get(), TCGETS, &t), SyscallSucceeds()); + EXPECT_EQ(t, DefaultTermios()); +} + +// Changing termios from the master actually affects the slave. +// +// TCSETS on the master actually redirects to the slave (see comment on +// MasterTermiosUnchangable). +TEST_F(PtyTest, TermiosAffectsSlave) { + struct kernel_termios master_termios = {}; + EXPECT_THAT(ioctl(master_.get(), TCGETS, &master_termios), SyscallSucceeds()); + master_termios.c_lflag ^= ICANON; + EXPECT_THAT(ioctl(master_.get(), TCSETS, &master_termios), SyscallSucceeds()); + + struct kernel_termios slave_termios = {}; + EXPECT_THAT(ioctl(slave_.get(), TCGETS, &slave_termios), SyscallSucceeds()); + EXPECT_EQ(master_termios, slave_termios); +} + +// The master end of the pty has termios: +// +// struct kernel_termios t = { +// .c_iflag = 0; +// .c_oflag = 0; +// .c_cflag = B38400 | CS8 | CREAD; +// .c_lflag = 0; +// .c_cc = /* same as DefaultTermios */ +// } +// +// (From drivers/tty/pty.c:unix98_pty_init) +// +// All termios control ioctls on the master actually redirect to the slave +// (drivers/tty/tty_ioctl.c:tty_mode_ioctl), making it impossible to change the +// master termios. +// +// Verify this by setting ICRNL (which rewrites input \r to \n) and verify that +// it has no effect on the master. +TEST_F(PtyTest, MasterTermiosUnchangable) { + char c = '\r'; + ASSERT_THAT(WriteFd(slave_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + ExpectReadable(master_, 1, &c); + EXPECT_EQ(c, '\r'); // ICRNL had no effect! + + ExpectFinished(master_); +} + +// ICRNL rewrites input \r to \n. +TEST_F(PtyTest, TermiosICRNL) { + struct kernel_termios t = DefaultTermios(); + t.c_iflag |= ICRNL; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + + char c = '\r'; + ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + ExpectReadable(slave_, 1, &c); + EXPECT_EQ(c, '\n'); + + ExpectFinished(slave_); +} + +// ONLCR rewrites output \n to \r\n. +TEST_F(PtyTest, TermiosONLCR) { + struct kernel_termios t = DefaultTermios(); + t.c_oflag |= ONLCR; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + + char c = '\n'; + ASSERT_THAT(WriteFd(slave_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + // Extra byte for NUL for EXPECT_STREQ. + char buf[3] = {}; + ExpectReadable(master_, 2, buf); + EXPECT_STREQ(buf, "\r\n"); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, TermiosIGNCR) { + struct kernel_termios t = DefaultTermios(); + t.c_iflag |= IGNCR; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + + char c = '\r'; + ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + // Nothing to read. + ASSERT_THAT(PollAndReadFd(slave_.get(), &c, 1, kTimeout), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); +} + +// Test that we can successfully poll for readable data from the slave. +TEST_F(PtyTest, TermiosPollSlave) { + struct kernel_termios t = DefaultTermios(); + t.c_iflag |= IGNCR; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + + absl::Notification notify; + int sfd = slave_.get(); + ScopedThread th([sfd, ¬ify]() { + notify.Notify(); + + // Poll on the reader fd with POLLIN event. + struct pollfd poll_fd = {sfd, POLLIN, 0}; + EXPECT_THAT( + RetryEINTR(poll)(&poll_fd, 1, absl::ToInt64Milliseconds(kTimeout)), + SyscallSucceedsWithValue(1)); + + // Should trigger POLLIN event. + EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); + }); + + notify.WaitForNotification(); + // Sleep ensures that poll begins waiting before we write to the FD. + absl::SleepFor(absl::Seconds(1)); + + char s[] = "foo\n"; + ASSERT_THAT(WriteFd(master_.get(), s, strlen(s) + 1), SyscallSucceeds()); +} + +// Test that we can successfully poll for readable data from the master. +TEST_F(PtyTest, TermiosPollMaster) { + struct kernel_termios t = DefaultTermios(); + t.c_iflag |= IGNCR; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(master_.get(), TCSETS, &t), SyscallSucceeds()); + + absl::Notification notify; + int mfd = master_.get(); + ScopedThread th([mfd, ¬ify]() { + notify.Notify(); + + // Poll on the reader fd with POLLIN event. + struct pollfd poll_fd = {mfd, POLLIN, 0}; + EXPECT_THAT( + RetryEINTR(poll)(&poll_fd, 1, absl::ToInt64Milliseconds(kTimeout)), + SyscallSucceedsWithValue(1)); + + // Should trigger POLLIN event. + EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); + }); + + notify.WaitForNotification(); + // Sleep ensures that poll begins waiting before we write to the FD. + absl::SleepFor(absl::Seconds(1)); + + char s[] = "foo\n"; + ASSERT_THAT(WriteFd(slave_.get(), s, strlen(s) + 1), SyscallSucceeds()); +} + +TEST_F(PtyTest, TermiosINLCR) { + struct kernel_termios t = DefaultTermios(); + t.c_iflag |= INLCR; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + + char c = '\n'; + ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + ExpectReadable(slave_, 1, &c); + EXPECT_EQ(c, '\r'); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, TermiosONOCR) { + struct kernel_termios t = DefaultTermios(); + t.c_oflag |= ONOCR; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + + // The terminal is at column 0, so there should be no CR to read. + char c = '\r'; + ASSERT_THAT(WriteFd(slave_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + // Nothing to read. + ASSERT_THAT(PollAndReadFd(master_.get(), &c, 1, kTimeout), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); + + // This time the column is greater than 0, so we should be able to read the CR + // out of the other end. + constexpr char kInput[] = "foo\r"; + constexpr int kInputSize = sizeof(kInput) - 1; + ASSERT_THAT(WriteFd(slave_.get(), kInput, kInputSize), + SyscallSucceedsWithValue(kInputSize)); + + char buf[kInputSize] = {}; + ExpectReadable(master_, kInputSize, buf); + + EXPECT_EQ(memcmp(buf, kInput, kInputSize), 0); + + ExpectFinished(master_); + + // Terminal should be at column 0 again, so no CR can be read. + ASSERT_THAT(WriteFd(slave_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + // Nothing to read. + ASSERT_THAT(PollAndReadFd(master_.get(), &c, 1, kTimeout), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); +} + +TEST_F(PtyTest, TermiosOCRNL) { + struct kernel_termios t = DefaultTermios(); + t.c_oflag |= OCRNL; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + + // The terminal is at column 0, so there should be no CR to read. + char c = '\r'; + ASSERT_THAT(WriteFd(slave_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + ExpectReadable(master_, 1, &c); + EXPECT_EQ(c, '\n'); + + ExpectFinished(master_); +} + +// Tests that VEOL is disabled when we start, and that we can set it to enable +// it. +TEST_F(PtyTest, VEOLTermination) { + // Write a few bytes ending with '\0', and confirm that we can't read. + constexpr char kInput[] = "hello"; + ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)), + SyscallSucceedsWithValue(sizeof(kInput))); + char buf[sizeof(kInput)] = {}; + ASSERT_THAT(PollAndReadFd(slave_.get(), buf, sizeof(kInput), kTimeout), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); + + // Set the EOL character to '=' and write it. + constexpr char delim = '='; + struct kernel_termios t = DefaultTermios(); + t.c_cc[VEOL] = delim; + ASSERT_THAT(ioctl(slave_.get(), TCSETS, &t), SyscallSucceeds()); + ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); + + // Now we can read, as sending EOL caused the line to become available. + ExpectReadable(slave_, sizeof(kInput), buf); + EXPECT_EQ(memcmp(buf, kInput, sizeof(kInput)), 0); + + ExpectReadable(slave_, 1, buf); + EXPECT_EQ(buf[0], '='); + + ExpectFinished(slave_); +} + +// Tests that we can write more than the 4096 character limit, then a +// terminating character, then read out just the first 4095 bytes plus the +// terminator. +TEST_F(PtyTest, CanonBigWrite) { + constexpr int kWriteLen = kMaxLineSize + 4; + char input[kWriteLen]; + memset(input, 'M', kWriteLen - 1); + input[kWriteLen - 1] = '\n'; + ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen), + SyscallSucceedsWithValue(kWriteLen)); + + // We can read the line. + char buf[kMaxLineSize] = {}; + ExpectReadable(slave_, kMaxLineSize, buf); + + ExpectFinished(slave_); +} + +// Tests that data written in canonical mode can be read immediately once +// switched to noncanonical mode. +TEST_F(PtyTest, SwitchCanonToNoncanon) { + // Write a few bytes without a terminating character, switch to noncanonical + // mode, and read them. + constexpr char kInput[] = "hello"; + ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)), + SyscallSucceedsWithValue(sizeof(kInput))); + + // Nothing available yet. + char buf[sizeof(kInput)] = {}; + ASSERT_THAT(PollAndReadFd(slave_.get(), buf, sizeof(kInput), kTimeout), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); + + DisableCanonical(); + + ExpectReadable(slave_, sizeof(kInput), buf); + EXPECT_STREQ(buf, kInput); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, SwitchCanonToNonCanonNewline) { + // Write a few bytes with a terminating character. + constexpr char kInput[] = "hello\n"; + ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)), + SyscallSucceedsWithValue(sizeof(kInput))); + + DisableCanonical(); + + // We can read the line. + char buf[sizeof(kInput)] = {}; + ExpectReadable(slave_, sizeof(kInput), buf); + EXPECT_STREQ(buf, kInput); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, SwitchNoncanonToCanonNewlineBig) { + DisableCanonical(); + + // Write more than the maximum line size, then write a delimiter. + constexpr int kWriteLen = 4100; + char input[kWriteLen]; + memset(input, 'M', kWriteLen); + ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen), + SyscallSucceedsWithValue(kWriteLen)); + // Wait for the input queue to fill. + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), kMaxLineSize - 1)); + constexpr char delim = '\n'; + ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); + + EnableCanonical(); + + // We can read the line. + char buf[kMaxLineSize] = {}; + ExpectReadable(slave_, kMaxLineSize - 1, buf); + + // We can also read the remaining characters. + ExpectReadable(slave_, 6, buf); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, SwitchNoncanonToCanonNoNewline) { + DisableCanonical(); + + // Write a few bytes without a terminating character. + // mode, and read them. + constexpr char kInput[] = "hello"; + ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput) - 1), + SyscallSucceedsWithValue(sizeof(kInput) - 1)); + + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), sizeof(kInput) - 1)); + EnableCanonical(); + + // We can read the line. + char buf[sizeof(kInput)] = {}; + ExpectReadable(slave_, sizeof(kInput) - 1, buf); + EXPECT_STREQ(buf, kInput); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, SwitchNoncanonToCanonNoNewlineBig) { + DisableCanonical(); + + // Write a few bytes without a terminating character. + // mode, and read them. + constexpr int kWriteLen = 4100; + char input[kWriteLen]; + memset(input, 'M', kWriteLen); + ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen), + SyscallSucceedsWithValue(kWriteLen)); + + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), kMaxLineSize - 1)); + EnableCanonical(); + + // We can read the line. + char buf[kMaxLineSize] = {}; + ExpectReadable(slave_, kMaxLineSize - 1, buf); + + ExpectFinished(slave_); +} + +// Tests that we can write over the 4095 noncanonical limit, then read out +// everything. +TEST_F(PtyTest, NoncanonBigWrite) { + DisableCanonical(); + + // Write well over the 4095 internal buffer limit. + constexpr char kInput = 'M'; + constexpr int kInputSize = kMaxLineSize * 2; + for (int i = 0; i < kInputSize; i++) { + // This makes too many syscalls for save/restore. + const DisableSave ds; + ASSERT_THAT(WriteFd(master_.get(), &kInput, sizeof(kInput)), + SyscallSucceedsWithValue(sizeof(kInput))); + } + + // We should be able to read out everything. Sleep a bit so that Linux has a + // chance to move data from the master to the slave. + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), kMaxLineSize - 1)); + for (int i = 0; i < kInputSize; i++) { + // This makes too many syscalls for save/restore. + const DisableSave ds; + char c; + ExpectReadable(slave_, 1, &c); + ASSERT_EQ(c, kInput); + } + + ExpectFinished(slave_); +} + +// ICANON doesn't make input available until a line delimiter is typed. +// +// Test newline. +TEST_F(PtyTest, TermiosICANONNewline) { + char input[3] = {'a', 'b', 'c'}; + ASSERT_THAT(WriteFd(master_.get(), input, sizeof(input)), + SyscallSucceedsWithValue(sizeof(input))); + + // Extra bytes for newline (written later) and NUL for EXPECT_STREQ. + char buf[5] = {}; + + // Nothing available yet. + ASSERT_THAT(PollAndReadFd(slave_.get(), buf, sizeof(input), kTimeout), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); + + char delim = '\n'; + ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); + + // Now it is available. + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), sizeof(input) + 1)); + ExpectReadable(slave_, sizeof(input) + 1, buf); + EXPECT_STREQ(buf, "abc\n"); + + ExpectFinished(slave_); +} + +// ICANON doesn't make input available until a line delimiter is typed. +// +// Test EOF (^D). +TEST_F(PtyTest, TermiosICANONEOF) { + char input[3] = {'a', 'b', 'c'}; + ASSERT_THAT(WriteFd(master_.get(), input, sizeof(input)), + SyscallSucceedsWithValue(sizeof(input))); + + // Extra byte for NUL for EXPECT_STREQ. + char buf[4] = {}; + + // Nothing available yet. + ASSERT_THAT(PollAndReadFd(slave_.get(), buf, sizeof(input), kTimeout), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); + char delim = ControlCharacter('D'); + ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); + + // Now it is available. Note that ^D is not included. + ExpectReadable(slave_, sizeof(input), buf); + EXPECT_STREQ(buf, "abc"); + + ExpectFinished(slave_); +} + +// ICANON limits us to 4096 bytes including a terminating character. Anything +// after and 4095th character is discarded (although still processed for +// signals and echoing). +TEST_F(PtyTest, CanonDiscard) { + constexpr char kInput = 'M'; + constexpr int kInputSize = 4100; + constexpr int kIter = 3; + + // A few times write more than the 4096 character maximum, then a newline. + constexpr char delim = '\n'; + for (int i = 0; i < kIter; i++) { + // This makes too many syscalls for save/restore. + const DisableSave ds; + for (int i = 0; i < kInputSize; i++) { + ASSERT_THAT(WriteFd(master_.get(), &kInput, sizeof(kInput)), + SyscallSucceedsWithValue(sizeof(kInput))); + } + ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); + } + + // There should be multiple truncated lines available to read. + for (int i = 0; i < kIter; i++) { + char buf[kInputSize] = {}; + ExpectReadable(slave_, kMaxLineSize, buf); + EXPECT_EQ(buf[kMaxLineSize - 1], delim); + EXPECT_EQ(buf[kMaxLineSize - 2], kInput); + } + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, CanonMultiline) { + constexpr char kInput1[] = "GO\n"; + constexpr char kInput2[] = "BLUE\n"; + + // Write both lines. + ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1), + SyscallSucceedsWithValue(sizeof(kInput1) - 1)); + ASSERT_THAT(WriteFd(master_.get(), kInput2, sizeof(kInput2) - 1), + SyscallSucceedsWithValue(sizeof(kInput2) - 1)); + + // Get the first line. + char line1[8] = {}; + ExpectReadable(slave_, sizeof(kInput1) - 1, line1); + EXPECT_STREQ(line1, kInput1); + + // Get the second line. + char line2[8] = {}; + ExpectReadable(slave_, sizeof(kInput2) - 1, line2); + EXPECT_STREQ(line2, kInput2); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, SwitchNoncanonToCanonMultiline) { + DisableCanonical(); + + constexpr char kInput1[] = "GO\n"; + constexpr char kInput2[] = "BLUE\n"; + constexpr char kExpected[] = "GO\nBLUE\n"; + + // Write both lines. + ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1), + SyscallSucceedsWithValue(sizeof(kInput1) - 1)); + ASSERT_THAT(WriteFd(master_.get(), kInput2, sizeof(kInput2) - 1), + SyscallSucceedsWithValue(sizeof(kInput2) - 1)); + + ASSERT_NO_ERRNO( + WaitUntilReceived(slave_.get(), sizeof(kInput1) + sizeof(kInput2) - 2)); + EnableCanonical(); + + // Get all together as one line. + char line[9] = {}; + ExpectReadable(slave_, 8, line); + EXPECT_STREQ(line, kExpected); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, SwitchTwiceMultiline) { + std::string kInputs[] = {"GO\n", "BLUE\n", "!"}; + std::string kExpected = "GO\nBLUE\n!"; + + // Write each line. + for (std::string input : kInputs) { + ASSERT_THAT(WriteFd(master_.get(), input.c_str(), input.size()), + SyscallSucceedsWithValue(input.size())); + } + + DisableCanonical(); + // All written characters have to make it into the input queue before + // canonical mode is re-enabled. If the final '!' character hasn't been + // enqueued before canonical mode is re-enabled, it won't be readable. + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), kExpected.size())); + EnableCanonical(); + + // Get all together as one line. + char line[10] = {}; + ExpectReadable(slave_, 9, line); + EXPECT_STREQ(line, kExpected.c_str()); + + ExpectFinished(slave_); +} + +TEST_F(PtyTest, QueueSize) { + // Write the line. + constexpr char kInput1[] = "GO\n"; + ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1), + SyscallSucceedsWithValue(sizeof(kInput1) - 1)); + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), sizeof(kInput1) - 1)); + + // Ensure that writing more (beyond what is readable) does not impact the + // readable size. + char input[kMaxLineSize]; + memset(input, 'M', kMaxLineSize); + ASSERT_THAT(WriteFd(master_.get(), input, kMaxLineSize), + SyscallSucceedsWithValue(kMaxLineSize)); + int inputBufSize = ASSERT_NO_ERRNO_AND_VALUE( + WaitUntilReceived(slave_.get(), sizeof(kInput1) - 1)); + EXPECT_EQ(inputBufSize, sizeof(kInput1) - 1); +} + +TEST_F(PtyTest, PartialBadBuffer) { + // Allocate 2 pages. + void* addr = mmap(nullptr, 2 * kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(addr, MAP_FAILED); + char* buf = reinterpret_cast(addr); + + // Guard the 2nd page for our read to run into. + ASSERT_THAT( + mprotect(reinterpret_cast(buf + kPageSize), kPageSize, PROT_NONE), + SyscallSucceeds()); + + // Leave only one free byte in the buffer. + char* bad_buffer = buf + kPageSize - 1; + + // Write to the master. + constexpr char kBuf[] = "hello\n"; + constexpr size_t size = sizeof(kBuf) - 1; + EXPECT_THAT(WriteFd(master_.get(), kBuf, size), + SyscallSucceedsWithValue(size)); + + // Read from the slave into bad_buffer. + ASSERT_NO_ERRNO(WaitUntilReceived(slave_.get(), size)); + EXPECT_THAT(ReadFd(slave_.get(), bad_buffer, size), + SyscallFailsWithErrno(EFAULT)); + + EXPECT_THAT(munmap(addr, 2 * kPageSize), SyscallSucceeds()) << addr; +} + +TEST_F(PtyTest, SimpleEcho) { + constexpr char kInput[] = "Mr. Eko"; + EXPECT_THAT(WriteFd(master_.get(), kInput, strlen(kInput)), + SyscallSucceedsWithValue(strlen(kInput))); + + char buf[100] = {}; + ExpectReadable(master_, strlen(kInput), buf); + + EXPECT_STREQ(buf, kInput); + ExpectFinished(master_); +} + +TEST_F(PtyTest, GetWindowSize) { + struct winsize ws; + ASSERT_THAT(ioctl(slave_.get(), TIOCGWINSZ, &ws), SyscallSucceeds()); + EXPECT_EQ(ws.ws_row, 0); + EXPECT_EQ(ws.ws_col, 0); +} + +TEST_F(PtyTest, SetSlaveWindowSize) { + constexpr uint16_t kRows = 343; + constexpr uint16_t kCols = 2401; + struct winsize ws = {.ws_row = kRows, .ws_col = kCols}; + ASSERT_THAT(ioctl(slave_.get(), TIOCSWINSZ, &ws), SyscallSucceeds()); + + struct winsize retrieved_ws = {}; + ASSERT_THAT(ioctl(master_.get(), TIOCGWINSZ, &retrieved_ws), + SyscallSucceeds()); + EXPECT_EQ(retrieved_ws.ws_row, kRows); + EXPECT_EQ(retrieved_ws.ws_col, kCols); +} + +TEST_F(PtyTest, SetMasterWindowSize) { + constexpr uint16_t kRows = 343; + constexpr uint16_t kCols = 2401; + struct winsize ws = {.ws_row = kRows, .ws_col = kCols}; + ASSERT_THAT(ioctl(master_.get(), TIOCSWINSZ, &ws), SyscallSucceeds()); + + struct winsize retrieved_ws = {}; + ASSERT_THAT(ioctl(slave_.get(), TIOCGWINSZ, &retrieved_ws), + SyscallSucceeds()); + EXPECT_EQ(retrieved_ws.ws_row, kRows); + EXPECT_EQ(retrieved_ws.ws_col, kCols); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/pwrite64.cc b/test/syscalls/linux/pwrite64.cc new file mode 100644 index 000000000..60ae6de1f --- /dev/null +++ b/test/syscalls/linux/pwrite64.cc @@ -0,0 +1,79 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// This test is currently very rudimentary. +// +// TODO: +// * bad buffer states (EFAULT). +// * bad fds (wrong permission, wrong type of file, EBADF). +// * check offset is not incremented. +// * check for EOF. +// * writing to pipes, symlinks, special files. +class Pwrite64 : public ::testing::Test { + void SetUp() override { + name_ = NewTempAbsPath(); + int fd; + ASSERT_THAT(fd = open(name_.c_str(), O_CREAT, 0644), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + } + + void TearDown() override { unlink(name_.c_str()); } + + public: + std::string name_; +}; + +TEST_F(Pwrite64, AppendOnly) { + int fd; + ASSERT_THAT(fd = open(name_.c_str(), O_APPEND | O_RDWR), SyscallSucceeds()); + constexpr int64_t kBufSize = 1024; + std::vector buf(kBufSize); + std::fill(buf.begin(), buf.end(), 'a'); + EXPECT_THAT(PwriteFd(fd, buf.data(), buf.size(), 0), + SyscallSucceedsWithValue(buf.size())); + EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(0)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST_F(Pwrite64, InvalidArgs) { + int fd; + ASSERT_THAT(fd = open(name_.c_str(), O_APPEND | O_RDWR), SyscallSucceeds()); + constexpr int64_t kBufSize = 1024; + std::vector buf(kBufSize); + std::fill(buf.begin(), buf.end(), 'a'); + EXPECT_THAT(PwriteFd(fd, buf.data(), buf.size(), -1), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/read.cc b/test/syscalls/linux/read.cc new file mode 100644 index 000000000..eb1b5bc10 --- /dev/null +++ b/test/syscalls/linux/read.cc @@ -0,0 +1,117 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class ReadTest : public ::testing::Test { + void SetUp() override { + name_ = NewTempAbsPath(); + int fd; + ASSERT_THAT(fd = open(name_.c_str(), O_CREAT, 0644), SyscallSucceeds()); + ASSERT_THAT(close(fd), SyscallSucceeds()); + } + + void TearDown() override { unlink(name_.c_str()); } + + public: + std::string name_; +}; + +TEST_F(ReadTest, ZeroBuffer) { + int fd; + ASSERT_THAT(fd = open(name_.c_str(), O_RDWR), SyscallSucceeds()); + + char msg[] = "hello world"; + EXPECT_THAT(PwriteFd(fd, msg, strlen(msg), 0), + SyscallSucceedsWithValue(strlen(msg))); + + char buf[10]; + EXPECT_THAT(ReadFd(fd, buf, 0), SyscallSucceedsWithValue(0)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST_F(ReadTest, EmptyFileReturnsZeroAtEOF) { + int fd; + ASSERT_THAT(fd = open(name_.c_str(), O_RDWR), SyscallSucceeds()); + + char eof_buf[10]; + EXPECT_THAT(ReadFd(fd, eof_buf, 10), SyscallSucceedsWithValue(0)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST_F(ReadTest, EofAfterRead) { + int fd; + ASSERT_THAT(fd = open(name_.c_str(), O_RDWR), SyscallSucceeds()); + + // Write some bytes to be read. + constexpr char kMessage[] = "hello world"; + EXPECT_THAT(PwriteFd(fd, kMessage, sizeof(kMessage), 0), + SyscallSucceedsWithValue(sizeof(kMessage))); + + // Read all of the bytes at once. + char buf[sizeof(kMessage)]; + EXPECT_THAT(ReadFd(fd, buf, sizeof(kMessage)), + SyscallSucceedsWithValue(sizeof(kMessage))); + + // Read again with a non-zero buffer and expect EOF. + char eof_buf[10]; + EXPECT_THAT(ReadFd(fd, eof_buf, 10), SyscallSucceedsWithValue(0)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST_F(ReadTest, DevNullReturnsEof) { + int fd; + ASSERT_THAT(fd = open("/dev/null", O_RDONLY), SyscallSucceeds()); + std::vector buf(1); + EXPECT_THAT(ReadFd(fd, buf.data(), 1), SyscallSucceedsWithValue(0)); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +const int kReadSize = 128 * 1024; + +// Do not allow random save as it could lead to partial reads. +TEST_F(ReadTest, CanReadFullyFromDevZero_NoRandomSave) { + int fd; + ASSERT_THAT(fd = open("/dev/zero", O_RDONLY), SyscallSucceeds()); + + std::vector buf(kReadSize, 1); + EXPECT_THAT(ReadFd(fd, buf.data(), kReadSize), + SyscallSucceedsWithValue(kReadSize)); + EXPECT_THAT(close(fd), SyscallSucceeds()); + EXPECT_EQ(std::vector(kReadSize, 0), buf); +} + +TEST_F(ReadTest, ReadDirectoryFails) { + const FileDescriptor file = + ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); + std::vector buf(1); + EXPECT_THAT(ReadFd(file.get(), buf.data(), 1), SyscallFailsWithErrno(EISDIR)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/readv.cc b/test/syscalls/linux/readv.cc new file mode 100644 index 000000000..0b933673a --- /dev/null +++ b/test/syscalls/linux/readv.cc @@ -0,0 +1,293 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/file_base.h" +#include "test/syscalls/linux/readv_common.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class ReadvTest : public FileTest { + void SetUp() override { + FileTest::SetUp(); + + ASSERT_THAT(write(test_file_fd_.get(), kReadvTestData, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + ASSERT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(write(test_pipe_[1], kReadvTestData, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + } +}; + +TEST_F(ReadvTest, ReadOneBufferPerByte_File) { + ReadOneBufferPerByte(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadOneBufferPerByte_Pipe) { + ReadOneBufferPerByte(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadOneHalfAtATime_File) { + ReadOneHalfAtATime(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadOneHalfAtATime_Pipe) { + ReadOneHalfAtATime(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadAllOneBuffer_File) { + ReadAllOneBuffer(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadAllOneBuffer_Pipe) { ReadAllOneBuffer(test_pipe_[0]); } + +TEST_F(ReadvTest, ReadAllOneLargeBuffer_File) { + ReadAllOneLargeBuffer(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadAllOneLargeBuffer_Pipe) { + ReadAllOneLargeBuffer(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadBuffersOverlapping_File) { + ReadBuffersOverlapping(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadBuffersOverlapping_Pipe) { + ReadBuffersOverlapping(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadBuffersDiscontinuous_File) { + ReadBuffersDiscontinuous(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadBuffersDiscontinuous_Pipe) { + ReadBuffersDiscontinuous(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadIovecsCompletelyFilled_File) { + ReadIovecsCompletelyFilled(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadIovecsCompletelyFilled_Pipe) { + ReadIovecsCompletelyFilled(test_pipe_[0]); +} + +TEST_F(ReadvTest, BadFileDescriptor) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + + ASSERT_THAT(readv(-1, iov, 1024), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(ReadvTest, BadIovecsPointer_File) { + ASSERT_THAT(readv(test_file_fd_.get(), nullptr, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, BadIovecsPointer_Pipe) { + ASSERT_THAT(readv(test_pipe_[0], nullptr, 1), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, BadIovecBase_File) { + struct iovec iov[1]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, BadIovecBase_Pipe) { + struct iovec iov[1]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, ZeroIovecs_File) { + struct iovec iov[1]; + iov[0].iov_base = 0; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), SyscallSucceeds()); +} + +TEST_F(ReadvTest, ZeroIovecs_Pipe) { + struct iovec iov[1]; + iov[0].iov_base = 0; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallSucceeds()); +} + +TEST_F(ReadvTest, NotReadable_File) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + + std::string wronly_file = NewTempAbsPath(); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(wronly_file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)); + ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EBADF)); + fd.reset(); // Close before unlinking. + ASSERT_THAT(unlink(wronly_file.c_str()), SyscallSucceeds()); +} + +TEST_F(ReadvTest, NotReadable_Pipe) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_pipe_[1], iov, 1), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(ReadvTest, DirNotReadable) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); + ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EISDIR)); +} + +TEST_F(ReadvTest, OffsetIncremented) { + char* buffer = reinterpret_cast(malloc(kReadvTestDataSize)); + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = kReadvTestDataSize; + + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + ASSERT_THAT(lseek(test_file_fd_.get(), 0, SEEK_CUR), + SyscallSucceedsWithValue(kReadvTestDataSize)); + + free(buffer); +} + +TEST_F(ReadvTest, EndOfFile) { + char* buffer = reinterpret_cast(malloc(kReadvTestDataSize)); + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + free(buffer); + + buffer = reinterpret_cast(malloc(kReadvTestDataSize)); + iov[0].iov_base = buffer; + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), SyscallSucceedsWithValue(0)); + free(buffer); +} + +TEST_F(ReadvTest, WouldBlock_Pipe) { + struct iovec iov[1]; + iov[0].iov_base = reinterpret_cast(malloc(kReadvTestDataSize)); + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + free(iov[0].iov_base); + + iov[0].iov_base = reinterpret_cast(malloc(kReadvTestDataSize)); + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallFailsWithErrno(EAGAIN)); + free(iov[0].iov_base); +} + +TEST_F(ReadvTest, ZeroBuffer) { + char buf[10]; + struct iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallSucceedsWithValue(0)); +} + +TEST_F(ReadvTest, NullIovecInNonemptyArray) { + std::vector buf(kReadvTestDataSize); + struct iovec iov[2]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 0; + iov[1].iov_base = buf.data(); + iov[1].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 2), + SyscallSucceedsWithValue(kReadvTestDataSize)); +} + +TEST_F(ReadvTest, IovecOutsideTaskAddressRangeInNonemptyArray) { + std::vector buf(kReadvTestDataSize); + struct iovec iov[2]; + iov[0].iov_base = reinterpret_cast(~static_cast(0)); + iov[0].iov_len = 0; + iov[1].iov_base = buf.data(); + iov[1].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 2), + SyscallFailsWithErrno(EFAULT)); +} + +// This test depends on the maximum extent of a single readv() syscall, so +// we can't tolerate interruption from saving. +TEST(ReadvTestNoFixture, TruncatedAtMax_NoRandomSave) { + // Ensure that we won't be interrupted by ITIMER_PROF. + struct itimerval itv = {}; + auto const cleanup_itimer = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_PROF, itv)); + + // From Linux's include/linux/fs.h. + size_t const MAX_RW_COUNT = INT_MAX & ~(kPageSize - 1); + + // Create an iovec array with 3 segments pointing to consecutive parts of a + // buffer. The first covers all but the last three pages, and should be + // written to in its entirety. The second covers the last page before + // MAX_RW_COUNT and the first page after; only the first page should be + // written to. The third covers the last page of the buffer, and should be + // skipped entirely. + size_t const kBufferSize = MAX_RW_COUNT + 2 * kPageSize; + size_t const kFirstOffset = MAX_RW_COUNT - kPageSize; + size_t const kSecondOffset = MAX_RW_COUNT + kPageSize; + // The buffer is too big to fit on the stack. + std::vector buf(kBufferSize); + struct iovec iov[3]; + iov[0].iov_base = buf.data(); + iov[0].iov_len = kFirstOffset; + iov[1].iov_base = buf.data() + kFirstOffset; + iov[1].iov_len = kSecondOffset - kFirstOffset; + iov[2].iov_base = buf.data() + kSecondOffset; + iov[2].iov_len = kBufferSize - kSecondOffset; + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); + EXPECT_THAT(readv(fd.get(), iov, 3), SyscallSucceedsWithValue(MAX_RW_COUNT)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/readv_common.cc b/test/syscalls/linux/readv_common.cc new file mode 100644 index 000000000..349b80d7f --- /dev/null +++ b/test/syscalls/linux/readv_common.cc @@ -0,0 +1,180 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/file_base.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +extern const char kReadvTestData[] = + "127.0.0.1 localhost" + "" + "# The following lines are desirable for IPv6 capable hosts" + "::1 ip6-localhost ip6-loopback" + "fe00::0 ip6-localnet" + "ff00::0 ip6-mcastprefix" + "ff02::1 ip6-allnodes" + "ff02::2 ip6-allrouters" + "ff02::3 ip6-allhosts" + "192.168.1.100 a" + "93.184.216.34 foo.bar.example.com xcpu"; +extern const size_t kReadvTestDataSize = sizeof(kReadvTestData); + +static void ReadAllOneProvidedBuffer(int fd, std::vector* buffer) { + struct iovec iovs[1]; + iovs[0].iov_base = buffer->data(); + iovs[0].iov_len = kReadvTestDataSize; + + ASSERT_THAT(readv(fd, iovs, 1), SyscallSucceedsWithValue(kReadvTestDataSize)); + + std::pair iovec_desc(iovs, 1); + EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); + EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); +} + +void ReadAllOneBuffer(int fd) { + std::vector buffer(kReadvTestDataSize); + ReadAllOneProvidedBuffer(fd, &buffer); +} + +void ReadAllOneLargeBuffer(int fd) { + std::vector buffer(10 * kReadvTestDataSize); + ReadAllOneProvidedBuffer(fd, &buffer); +} + +void ReadOneHalfAtATime(int fd) { + int len0 = kReadvTestDataSize / 2; + int len1 = kReadvTestDataSize - len0; + std::vector buffer0(len0); + std::vector buffer1(len1); + + struct iovec iovs[2]; + iovs[0].iov_base = buffer0.data(); + iovs[0].iov_len = len0; + iovs[1].iov_base = buffer1.data(); + iovs[1].iov_len = len1; + + ASSERT_THAT(readv(fd, iovs, 2), SyscallSucceedsWithValue(kReadvTestDataSize)); + + std::pair iovec_desc(iovs, 2); + EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); + EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); +} + +void ReadOneBufferPerByte(int fd) { + std::vector buffer(kReadvTestDataSize); + std::vector iovs(kReadvTestDataSize); + char* buffer_ptr = buffer.data(); + struct iovec* iovs_ptr = iovs.data(); + + for (int i = 0; i < static_cast(kReadvTestDataSize); i++) { + struct iovec iov = { + .iov_base = &buffer_ptr[i], + .iov_len = 1, + }; + iovs_ptr[i] = iov; + } + + ASSERT_THAT(readv(fd, iovs_ptr, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + + std::pair iovec_desc(iovs.data(), kReadvTestDataSize); + EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); + EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); +} + +void ReadBuffersOverlapping(int fd) { + // overlap the first overlap_bytes. + int overlap_bytes = 8; + std::vector buffer(kReadvTestDataSize); + + // overlapping causes us to get more data. + int expected_size = kReadvTestDataSize + overlap_bytes; + std::vector expected(expected_size); + char* expected_ptr = expected.data(); + memcpy(expected_ptr, &kReadvTestData[overlap_bytes], overlap_bytes); + memcpy(&expected_ptr[overlap_bytes], &kReadvTestData[overlap_bytes], + kReadvTestDataSize); + + struct iovec iovs[2]; + iovs[0].iov_base = buffer.data(); + iovs[0].iov_len = overlap_bytes; + iovs[1].iov_base = buffer.data(); + iovs[1].iov_len = kReadvTestDataSize; + + ASSERT_THAT(readv(fd, iovs, 2), SyscallSucceedsWithValue(kReadvTestDataSize)); + + std::pair iovec_desc(iovs, 2); + EXPECT_THAT(iovec_desc, MatchesStringLength(expected_size)); + EXPECT_THAT(iovec_desc, MatchesStringValue(expected_ptr)); +} + +void ReadBuffersDiscontinuous(int fd) { + // Each iov is 1 byte separated by 1 byte. + std::vector buffer(kReadvTestDataSize * 2); + std::vector iovs(kReadvTestDataSize); + + char* buffer_ptr = buffer.data(); + struct iovec* iovs_ptr = iovs.data(); + + for (int i = 0; i < static_cast(kReadvTestDataSize); i++) { + struct iovec iov = { + .iov_base = &buffer_ptr[i * 2], + .iov_len = 1, + }; + iovs_ptr[i] = iov; + } + + ASSERT_THAT(readv(fd, iovs_ptr, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + + std::pair iovec_desc(iovs.data(), kReadvTestDataSize); + EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); + EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); +} + +void ReadIovecsCompletelyFilled(int fd) { + int half = kReadvTestDataSize / 2; + std::vector buffer(kReadvTestDataSize); + char* buffer_ptr = buffer.data(); + memset(buffer.data(), '\0', kReadvTestDataSize); + + struct iovec iovs[2]; + iovs[0].iov_base = buffer.data(); + iovs[0].iov_len = half; + iovs[1].iov_base = &buffer_ptr[half]; + iovs[1].iov_len = half; + + ASSERT_THAT(readv(fd, iovs, 2), SyscallSucceedsWithValue(half * 2)); + + std::pair iovec_desc(iovs, 2); + EXPECT_THAT(iovec_desc, MatchesStringLength(half * 2)); + EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); + + char* str = static_cast(iovs[0].iov_base); + str[iovs[0].iov_len - 1] = '\0'; + ASSERT_EQ(half - 1, strlen(str)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/readv_common.h b/test/syscalls/linux/readv_common.h new file mode 100644 index 000000000..e261d545a --- /dev/null +++ b/test/syscalls/linux/readv_common.h @@ -0,0 +1,61 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_READV_COMMON_H_ +#define GVISOR_TEST_SYSCALLS_READV_COMMON_H_ + +#include + +namespace gvisor { +namespace testing { + +// A NUL-terminated std::string containing the data used by tests using the following +// test helpers. +extern const char kReadvTestData[]; + +// The size of kReadvTestData, including the terminating NUL. +extern const size_t kReadvTestDataSize; + +// ReadAllOneBuffer asserts that it can read kReadvTestData from an fd using +// exactly one iovec. +void ReadAllOneBuffer(int fd); + +// ReadAllOneLargeBuffer asserts that it can read kReadvTestData from an fd +// using exactly one iovec containing an overly large buffer. +void ReadAllOneLargeBuffer(int fd); + +// ReadOneHalfAtATime asserts that it can read test_data_from an fd using +// exactly two iovecs that are roughly equivalent in size. +void ReadOneHalfAtATime(int fd); + +// ReadOneBufferPerByte asserts that it can read kReadvTestData from an fd +// using one iovec per byte. +void ReadOneBufferPerByte(int fd); + +// ReadBuffersOverlapping asserts that it can read kReadvTestData from an fd +// where two iovecs are overlapping. +void ReadBuffersOverlapping(int fd); + +// ReadBuffersDiscontinuous asserts that it can read kReadvTestData from an fd +// where each iovec is discontinuous from the next by 1 byte. +void ReadBuffersDiscontinuous(int fd); + +// ReadIovecsCompletelyFilled asserts that the previous iovec is completely +// filled before moving onto the next. +void ReadIovecsCompletelyFilled(int fd); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_READV_COMMON_H_ diff --git a/test/syscalls/linux/readv_socket.cc b/test/syscalls/linux/readv_socket.cc new file mode 100644 index 000000000..2c129b7e8 --- /dev/null +++ b/test/syscalls/linux/readv_socket.cc @@ -0,0 +1,182 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/file_base.h" +#include "test/syscalls/linux/readv_common.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class ReadvSocketTest : public SocketTest { + void SetUp() override { + SocketTest::SetUp(); + ASSERT_THAT( + write(test_unix_stream_socket_[1], kReadvTestData, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + ASSERT_THAT( + write(test_unix_dgram_socket_[1], kReadvTestData, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + ASSERT_THAT(write(test_unix_seqpacket_socket_[1], kReadvTestData, + kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + // FIXME: Enable when possible. + // ASSERT_THAT(write(test_tcp_socket_[1], kReadvTestData, + // kReadvTestDataSize), + // SyscallSucceedsWithValue(kReadvTestDataSize)); + } +}; + +TEST_F(ReadvSocketTest, ReadOneBufferPerByte_StreamSocket) { + ReadOneBufferPerByte(test_unix_stream_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadOneBufferPerByte_DgramSocket) { + ReadOneBufferPerByte(test_unix_dgram_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadOneBufferPerByte_SeqPacketSocket) { + ReadOneBufferPerByte(test_unix_seqpacket_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadOneHalfAtATime_StreamSocket) { + ReadOneHalfAtATime(test_unix_stream_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadOneHalfAtATime_DgramSocket) { + ReadOneHalfAtATime(test_unix_dgram_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadAllOneBuffer_StreamSocket) { + ReadAllOneBuffer(test_unix_stream_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadAllOneBuffer_DgramSocket) { + ReadAllOneBuffer(test_unix_dgram_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadAllOneLargeBuffer_StreamSocket) { + ReadAllOneLargeBuffer(test_unix_stream_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadAllOneLargeBuffer_DgramSocket) { + ReadAllOneLargeBuffer(test_unix_dgram_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadBuffersOverlapping_StreamSocket) { + ReadBuffersOverlapping(test_unix_stream_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadBuffersOverlapping_DgramSocket) { + ReadBuffersOverlapping(test_unix_dgram_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadBuffersDiscontinuous_StreamSocket) { + ReadBuffersDiscontinuous(test_unix_stream_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadBuffersDiscontinuous_DgramSocket) { + ReadBuffersDiscontinuous(test_unix_dgram_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadIovecsCompletelyFilled_StreamSocket) { + ReadIovecsCompletelyFilled(test_unix_stream_socket_[0]); +} + +TEST_F(ReadvSocketTest, ReadIovecsCompletelyFilled_DgramSocket) { + ReadIovecsCompletelyFilled(test_unix_dgram_socket_[0]); +} + +TEST_F(ReadvSocketTest, BadIovecsPointer_StreamSocket) { + ASSERT_THAT(readv(test_unix_stream_socket_[0], nullptr, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvSocketTest, BadIovecsPointer_DgramSocket) { + ASSERT_THAT(readv(test_unix_dgram_socket_[0], nullptr, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvSocketTest, BadIovecBase_StreamSocket) { + struct iovec iov[1]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvSocketTest, BadIovecBase_DgramSocket) { + struct iovec iov[1]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvSocketTest, ZeroIovecs_StreamSocket) { + struct iovec iov[1]; + iov[0].iov_base = 0; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), SyscallSucceeds()); +} + +TEST_F(ReadvSocketTest, ZeroIovecs_DgramSocket) { + struct iovec iov[1]; + iov[0].iov_base = 0; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), SyscallSucceeds()); +} + +TEST_F(ReadvSocketTest, WouldBlock_StreamSocket) { + struct iovec iov[1]; + iov[0].iov_base = reinterpret_cast(malloc(kReadvTestDataSize)); + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + free(iov[0].iov_base); + + iov[0].iov_base = reinterpret_cast(malloc(kReadvTestDataSize)); + ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), + SyscallFailsWithErrno(EAGAIN)); + free(iov[0].iov_base); +} + +TEST_F(ReadvSocketTest, WouldBlock_DgramSocket) { + struct iovec iov[1]; + iov[0].iov_base = reinterpret_cast(malloc(kReadvTestDataSize)); + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + free(iov[0].iov_base); + + iov[0].iov_base = reinterpret_cast(malloc(kReadvTestDataSize)); + ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), + SyscallFailsWithErrno(EAGAIN)); + free(iov[0].iov_base); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/rename.cc b/test/syscalls/linux/rename.cc new file mode 100644 index 000000000..f4c877a00 --- /dev/null +++ b/test/syscalls/linux/rename.cc @@ -0,0 +1,373 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/util/capability_util.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(RenameTest, RootToAnything) { + ASSERT_THAT(rename("/", "/bin"), SyscallFailsWithErrno(EBUSY)); +} + +TEST(RenameTest, AnythingToRoot) { + ASSERT_THAT(rename("/bin", "/"), SyscallFailsWithErrno(EBUSY)); +} + +TEST(RenameTest, SourceIsAncestorOfTarget) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto subdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); + ASSERT_THAT(rename(dir.path().c_str(), subdir.path().c_str()), + SyscallFailsWithErrno(EINVAL)); + + // Try an even deeper directory. + auto deep_subdir = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(subdir.path())); + ASSERT_THAT(rename(dir.path().c_str(), deep_subdir.path().c_str()), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(RenameTest, TargetIsAncestorOfSource) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto subdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); + ASSERT_THAT(rename(subdir.path().c_str(), dir.path().c_str()), + SyscallFailsWithErrno(ENOTEMPTY)); + + // Try an even deeper directory. + auto deep_subdir = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(subdir.path())); + ASSERT_THAT(rename(deep_subdir.path().c_str(), dir.path().c_str()), + SyscallFailsWithErrno(ENOTEMPTY)); +} + +TEST(RenameTest, FileToSelf) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + EXPECT_THAT(rename(f.path().c_str(), f.path().c_str()), SyscallSucceeds()); +} + +TEST(RenameTest, DirectoryToSelf) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(rename(f.path().c_str(), f.path().c_str()), SyscallSucceeds()); +} + +TEST(RenameTest, FileToSameDirectory) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + std::string const newpath = NewTempAbsPath(); + ASSERT_THAT(rename(f.path().c_str(), newpath.c_str()), SyscallSucceeds()); + std::string const oldpath = f.release(); + f.reset(newpath); + EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); + EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); +} + +TEST(RenameTest, DirectoryToSameDirectory) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + std::string const newpath = NewTempAbsPath(); + ASSERT_THAT(rename(dir.path().c_str(), newpath.c_str()), SyscallSucceeds()); + std::string const oldpath = dir.release(); + dir.reset(newpath); + EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); + EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); +} + +TEST(RenameTest, FileToParentDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir2.path())); + std::string const newpath = NewTempAbsPathInDir(dir1.path()); + ASSERT_THAT(rename(f.path().c_str(), newpath.c_str()), SyscallSucceeds()); + std::string const oldpath = f.release(); + f.reset(newpath); + EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); + EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); +} + +TEST(RenameTest, DirectoryToParentDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); + auto dir3 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir2.path())); + EXPECT_THAT(IsDirectory(dir3.path()), IsPosixErrorOkAndHolds(true)); + std::string const newpath = NewTempAbsPathInDir(dir1.path()); + ASSERT_THAT(rename(dir3.path().c_str(), newpath.c_str()), SyscallSucceeds()); + std::string const oldpath = dir3.release(); + dir3.reset(newpath); + EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); + EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); + EXPECT_THAT(IsDirectory(newpath), IsPosixErrorOkAndHolds(true)); +} + +TEST(RenameTest, FileToChildDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + std::string const newpath = NewTempAbsPathInDir(dir2.path()); + ASSERT_THAT(rename(f.path().c_str(), newpath.c_str()), SyscallSucceeds()); + std::string const oldpath = f.release(); + f.reset(newpath); + EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); + EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); +} + +TEST(RenameTest, DirectoryToChildDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); + auto dir3 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); + std::string const newpath = NewTempAbsPathInDir(dir2.path()); + ASSERT_THAT(rename(dir3.path().c_str(), newpath.c_str()), SyscallSucceeds()); + std::string const oldpath = dir3.release(); + dir3.reset(newpath); + EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); + EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); + EXPECT_THAT(IsDirectory(newpath), IsPosixErrorOkAndHolds(true)); +} + +TEST(RenameTest, DirectoryToOwnChildDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); + std::string const newpath = NewTempAbsPathInDir(dir2.path()); + ASSERT_THAT(rename(dir1.path().c_str(), newpath.c_str()), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(RenameTest, FileOverwritesFile) { + auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), "first", TempPath::kDefaultFileMode)); + auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), "second", TempPath::kDefaultFileMode)); + ASSERT_THAT(rename(f1.path().c_str(), f2.path().c_str()), SyscallSucceeds()); + EXPECT_THAT(Exists(f1.path()), IsPosixErrorOkAndHolds(false)); + + f1.release(); + std::string f2_contents; + ASSERT_NO_ERRNO(GetContents(f2.path(), &f2_contents)); + EXPECT_EQ("first", f2_contents); +} + +TEST(RenameTest, FileDoesNotExist) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string source = JoinPath(dir.path(), "source"); + const std::string dest = JoinPath(dir.path(), "dest"); + ASSERT_THAT(rename(source.c_str(), dest.c_str()), + SyscallFailsWithErrno(ENOENT)); +} + +TEST(RenameTest, FileDoesNotOverwriteDirectory) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + ASSERT_THAT(rename(f.path().c_str(), dir.path().c_str()), + SyscallFailsWithErrno(EISDIR)); +} + +TEST(RenameTest, DirectoryDoesNotOverwriteFile) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + ASSERT_THAT(rename(dir.path().c_str(), f.path().c_str()), + SyscallFailsWithErrno(ENOTDIR)); +} + +TEST(RenameTest, DirectoryOverwritesEmptyDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()), + SyscallSucceeds()); + EXPECT_THAT(Exists(dir1.path()), IsPosixErrorOkAndHolds(false)); + dir1.release(); + EXPECT_THAT(Exists(JoinPath(dir2.path(), Basename(f.path()))), + IsPosixErrorOkAndHolds(true)); + f.release(); +} + +TEST(RenameTest, FailsWithDots) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto dir1_dot = absl::StrCat(dir1.path(), "/."); + auto dir2_dot = absl::StrCat(dir2.path(), "/."); + auto dir1_dot_dot = absl::StrCat(dir1.path(), "/.."); + auto dir2_dot_dot = absl::StrCat(dir2.path(), "/.."); + + // Try with dot paths in the first argument + EXPECT_THAT(rename(dir1_dot.c_str(), dir2.path().c_str()), + SyscallFailsWithErrno(EBUSY)); + EXPECT_THAT(rename(dir1_dot_dot.c_str(), dir2.path().c_str()), + SyscallFailsWithErrno(EBUSY)); + + // Try with dot paths in the second argument + EXPECT_THAT(rename(dir1.path().c_str(), dir2_dot.c_str()), + SyscallFailsWithErrno(EBUSY)); + EXPECT_THAT(rename(dir1.path().c_str(), dir2_dot_dot.c_str()), + SyscallFailsWithErrno(EBUSY)); +} + +TEST(RenameTest, DirectoryDoesNotOverwriteNonemptyDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir2.path())); + ASSERT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()), + SyscallFailsWithErrno(ENOTEMPTY)); +} + +TEST(RenameTest, FailsWhenOldParentNotWritable) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + // dir1 is not writable. + ASSERT_THAT(chmod(dir1.path().c_str(), 0555), SyscallSucceeds()); + + std::string const newpath = NewTempAbsPathInDir(dir2.path()); + EXPECT_THAT(rename(f1.path().c_str(), newpath.c_str()), + SyscallFailsWithErrno(EACCES)); +} + +TEST(RenameTest, FailsWhenNewParentNotWritable) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + // dir2 is not writable. + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555)); + + std::string const newpath = NewTempAbsPathInDir(dir2.path()); + EXPECT_THAT(rename(f1.path().c_str(), newpath.c_str()), + SyscallFailsWithErrno(EACCES)); +} + +// Equivalent to FailsWhenNewParentNotWritable, but with a destination file +// to overwrite. +TEST(RenameTest, OverwriteFailsWhenNewParentNotWritable) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + + // dir2 is not writable. + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir2.path())); + ASSERT_THAT(chmod(dir2.path().c_str(), 0555), SyscallSucceeds()); + + EXPECT_THAT(rename(f1.path().c_str(), f2.path().c_str()), + SyscallFailsWithErrno(EACCES)); +} + +// If the parent directory of source is not accessible, rename returns EACCES +// because the user cannot determine if source exists. +TEST(RenameTest, FileDoesNotExistWhenNewParentNotExecutable) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + // No execute permission. + auto dir = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0400)); + + const std::string source = JoinPath(dir.path(), "source"); + const std::string dest = JoinPath(dir.path(), "dest"); + ASSERT_THAT(rename(source.c_str(), dest.c_str()), + SyscallFailsWithErrno(EACCES)); +} + +TEST(RenameTest, DirectoryWithOpenFdOverwritesEmptyDirectory) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + // Get an fd on dir1 + int fd; + ASSERT_THAT(fd = open(dir1.path().c_str(), O_DIRECTORY), SyscallSucceeds()); + auto close_f = Cleanup([fd] { + // Close the fd on f. + EXPECT_THAT(close(fd), SyscallSucceeds()); + }); + + EXPECT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()), + SyscallSucceeds()); + + const std::string new_f_path = JoinPath(dir2.path(), Basename(f.path())); + + auto remove_f = Cleanup([&] { + // Delete f in its new location. + ASSERT_NO_ERRNO(Delete(new_f_path)); + f.release(); + }); + + EXPECT_THAT(Exists(dir1.path()), IsPosixErrorOkAndHolds(false)); + dir1.release(); + EXPECT_THAT(Exists(new_f_path), IsPosixErrorOkAndHolds(true)); +} + +TEST(RenameTest, FileWithOpenFd) { + TempPath root_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + TempPath dir1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); + TempPath dir2 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); + TempPath dir3 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); + + // Create file in dir1. + constexpr char kContents[] = "foo"; + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + dir1.path(), kContents, TempPath::kDefaultFileMode)); + + // Get fd on file. + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); + + // Move f to dir2. + const std::string path2 = NewTempAbsPathInDir(dir2.path()); + ASSERT_THAT(rename(f.path().c_str(), path2.c_str()), SyscallSucceeds()); + + // Read f's kContents. + char buf[sizeof(kContents)]; + EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(kContents), 0), + SyscallSucceedsWithValue(sizeof(kContents) - 1)); + EXPECT_EQ(absl::string_view(buf, sizeof(buf) - 1), kContents); + + // Move f to dir3. + const std::string path3 = NewTempAbsPathInDir(dir3.path()); + ASSERT_THAT(rename(path2.c_str(), path3.c_str()), SyscallSucceeds()); + + // Read f's kContents. + EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(kContents), 0), + SyscallSucceedsWithValue(sizeof(kContents) - 1)); + EXPECT_EQ(absl::string_view(buf, sizeof(buf) - 1), kContents); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/rlimits.cc b/test/syscalls/linux/rlimits.cc new file mode 100644 index 000000000..0072285f9 --- /dev/null +++ b/test/syscalls/linux/rlimits.cc @@ -0,0 +1,61 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "test/util/capability_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(RlimitTest, SetRlimitHigher) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))); + SKIP_IF(!IsRunningOnGvisor()); + + struct rlimit rl = {}; + EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); + + // Even with CAP_SYS_RESOURCE, gVisor does not allow setting a higher rlimit. + rl.rlim_max++; + EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EPERM)); +} + +TEST(RlimitTest, UnprivilegedSetRlimit) { + // Drop privileges if necessary. + if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))) { + EXPECT_NO_ERRNO(SetCapability(CAP_SYS_RESOURCE, false)); + } + + struct rlimit rl = {}; + rl.rlim_cur = 1000; + rl.rlim_max = 20000; + EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); + + struct rlimit rl2 = {}; + EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl2), SyscallSucceeds()); + EXPECT_EQ(rl.rlim_cur, rl2.rlim_cur); + EXPECT_EQ(rl.rlim_max, rl2.rlim_max); + + rl.rlim_max = 100000; + EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EPERM)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/rtsignal.cc b/test/syscalls/linux/rtsignal.cc new file mode 100644 index 000000000..1f2fed7cc --- /dev/null +++ b/test/syscalls/linux/rtsignal.cc @@ -0,0 +1,172 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/cleanup.h" +#include "test/util/logging.h" +#include "test/util/posix_error.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// saved_info is set by the handler. +siginfo_t saved_info; + +// has_saved_info is set to true by the handler. +volatile bool has_saved_info; + +void SigHandler(int sig, siginfo_t* info, void* context) { + // Copy to the given info. + saved_info = *info; + has_saved_info = true; +} + +void ClearSavedInfo() { + // Clear the cached info. + memset(&saved_info, 0, sizeof(saved_info)); + has_saved_info = false; +} + +PosixErrorOr SetupSignalHandler(int sig) { + struct sigaction sa; + sa.sa_sigaction = SigHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + return ScopedSigaction(sig, sa); +} + +class RtSignalTest : public ::testing::Test { + protected: + void SetUp() override { + action_cleanup_ = ASSERT_NO_ERRNO_AND_VALUE(SetupSignalHandler(SIGUSR1)); + mask_cleanup_ = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGUSR1)); + } + + void TearDown() override { ClearSavedInfo(); } + + private: + Cleanup action_cleanup_; + Cleanup mask_cleanup_; +}; + +static int rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t* uinfo) { + int ret; + do { + // NOTE: rt_sigqueueinfo(2) could return EAGAIN for RT signals. + ret = syscall(SYS_rt_sigqueueinfo, tgid, sig, uinfo); + } while (ret == -1 && errno == EAGAIN); + return ret; +} + +TEST_F(RtSignalTest, InvalidTID) { + siginfo_t uinfo; + // Depending on the kernel version, these calls may fail with + // ESRCH (goobunutu machines) or EPERM (production machines). Thus, + // the test simply ensures that they do fail. + EXPECT_THAT(rt_sigqueueinfo(-1, SIGUSR1, &uinfo), SyscallFails()); + EXPECT_FALSE(has_saved_info); + EXPECT_THAT(rt_sigqueueinfo(0, SIGUSR1, &uinfo), SyscallFails()); + EXPECT_FALSE(has_saved_info); +} + +TEST_F(RtSignalTest, InvalidCodes) { + siginfo_t uinfo; + + // We need a child for the code checks to apply. If the process is delivering + // to itself, then it can use whatever codes it wants and they will go + // through. + pid_t child = fork(); + if (child == 0) { + _exit(1); + } + ASSERT_THAT(child, SyscallSucceeds()); + + // These are not allowed for child processes. + uinfo.si_code = 0; // SI_USER. + EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), + SyscallFailsWithErrno(EPERM)); + uinfo.si_code = 0x80; // SI_KERNEL. + EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), + SyscallFailsWithErrno(EPERM)); + uinfo.si_code = -6; // SI_TKILL. + EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), + SyscallFailsWithErrno(EPERM)); + uinfo.si_code = -1; // SI_QUEUE (allowed). + EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), SyscallSucceeds()); + + // Join the child process. + EXPECT_THAT(waitpid(child, nullptr, 0), SyscallSucceeds()); +} + +TEST_F(RtSignalTest, ValueDelivered) { + siginfo_t uinfo; + uinfo.si_code = -1; // SI_QUEUE (allowed). + uinfo.si_errno = 0x1234; + + EXPECT_EQ(saved_info.si_errno, 0x0); + EXPECT_THAT(rt_sigqueueinfo(getpid(), SIGUSR1, &uinfo), SyscallSucceeds()); + EXPECT_TRUE(has_saved_info); + EXPECT_EQ(saved_info.si_errno, 0x1234); +} + +TEST_F(RtSignalTest, SignoMatch) { + auto action2_cleanup = ASSERT_NO_ERRNO_AND_VALUE(SetupSignalHandler(SIGUSR2)); + auto mask2_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGUSR2)); + + siginfo_t uinfo; + uinfo.si_code = -1; // SI_QUEUE (allowed). + + EXPECT_THAT(rt_sigqueueinfo(getpid(), SIGUSR1, &uinfo), SyscallSucceeds()); + EXPECT_TRUE(has_saved_info); + EXPECT_EQ(saved_info.si_signo, SIGUSR1); + + ClearSavedInfo(); + + EXPECT_THAT(rt_sigqueueinfo(getpid(), SIGUSR2, &uinfo), SyscallSucceeds()); + EXPECT_TRUE(has_saved_info); + EXPECT_EQ(saved_info.si_signo, SIGUSR2); +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + // These tests depend on delivering SIGUSR1/2 to the main thread (so they can + // synchronously check has_saved_info). Block these so that any other threads + // created by TestInit will also have them blocked. + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + sigaddset(&set, SIGUSR2); + TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); + + gvisor::testing::TestInit(&argc, &argv); + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/sched.cc b/test/syscalls/linux/sched.cc new file mode 100644 index 000000000..60cb6c443 --- /dev/null +++ b/test/syscalls/linux/sched.cc @@ -0,0 +1,71 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// In linux, pid is limited to 29 bits because how futex is implemented. +constexpr int kImpossiblePID = (1 << 29) + 1; + +TEST(SchedGetparamTest, ReturnsZero) { + struct sched_param param; + EXPECT_THAT(sched_getparam(getpid(), ¶m), SyscallSucceeds()); + EXPECT_EQ(param.sched_priority, 0); + EXPECT_THAT(sched_getparam(/*pid=*/0, ¶m), SyscallSucceeds()); + EXPECT_EQ(param.sched_priority, 0); +} + +TEST(SchedGetparamTest, InvalidPIDReturnsEINVAL) { + struct sched_param param; + EXPECT_THAT(sched_getparam(/*pid=*/-1, ¶m), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(SchedGetparamTest, ImpossiblePIDReturnsESRCH) { + struct sched_param param; + EXPECT_THAT(sched_getparam(kImpossiblePID, ¶m), + SyscallFailsWithErrno(ESRCH)); +} + +TEST(SchedGetparamTest, NullParamReturnsEINVAL) { + EXPECT_THAT(sched_getparam(0, nullptr), SyscallFailsWithErrno(EINVAL)); +} + +TEST(SchedGetschedulerTest, ReturnsSchedOther) { + EXPECT_THAT(sched_getscheduler(getpid()), + SyscallSucceedsWithValue(SCHED_OTHER)); + EXPECT_THAT(sched_getscheduler(/*pid=*/0), + SyscallSucceedsWithValue(SCHED_OTHER)); +} + +TEST(SchedGetschedulerTest, ReturnsEINVAL) { + EXPECT_THAT(sched_getscheduler(/*pid=*/-1), SyscallFailsWithErrno(EINVAL)); +} + +TEST(SchedGetschedulerTest, ReturnsESRCH) { + EXPECT_THAT(sched_getscheduler(kImpossiblePID), SyscallFailsWithErrno(ESRCH)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sched_yield.cc b/test/syscalls/linux/sched_yield.cc new file mode 100644 index 000000000..fc45aa5c2 --- /dev/null +++ b/test/syscalls/linux/sched_yield.cc @@ -0,0 +1,33 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(SchedYieldTest, Success) { + EXPECT_THAT(sched_yield(), SyscallSucceeds()); + EXPECT_THAT(sched_yield(), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/seccomp.cc b/test/syscalls/linux/seccomp.cc new file mode 100644 index 000000000..d6ac166a4 --- /dev/null +++ b/test/syscalls/linux/seccomp.cc @@ -0,0 +1,374 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/macros.h" +#include "test/util/logging.h" +#include "test/util/memory_util.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/proc_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +#ifndef SYS_SECCOMP +#define SYS_SECCOMP 1 +#endif + +namespace gvisor { +namespace testing { + +namespace { + +// A syscall not implemented by Linux that we don't expect to be called. +constexpr uint32_t kFilteredSyscall = SYS_vserver; + +// Applies a seccomp-bpf filter that returns `filtered_result` for +// `sysno` and allows all other syscalls. Async-signal-safe. +void ApplySeccompFilter(uint32_t sysno, uint32_t filtered_result, + uint32_t flags = 0) { + // "Prior to [PR_SET_SECCOMP], the task must call prctl(PR_SET_NO_NEW_PRIVS, + // 1) or run with CAP_SYS_ADMIN privileges in its namespace." - + // Documentation/prctl/seccomp_filter.txt + // + // prctl(PR_SET_NO_NEW_PRIVS, 1) may be called repeatedly; calls after the + // first are no-ops. + TEST_PCHECK(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0); + MaybeSave(); + + struct sock_filter filter[] = { + // A = seccomp_data.arch + BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 4), + // if (A != AUDIT_ARCH_X86_64) goto kill + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 4), + // A = seccomp_data.nr + BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0), + // if (A != sysno) goto allow + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, sysno, 0, 1), + // return filtered_result + BPF_STMT(BPF_RET | BPF_K, filtered_result), + // allow: return SECCOMP_RET_ALLOW + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + // kill: return SECCOMP_RET_KILL + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), + }; + struct sock_fprog prog; + prog.len = ABSL_ARRAYSIZE(filter); + prog.filter = filter; + if (flags) { + TEST_CHECK(syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, flags, &prog) == + 0); + } else { + TEST_PCHECK(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0) == 0); + } + MaybeSave(); +} + +// Wrapper for sigaction. Async-signal-safe. +void RegisterSignalHandler(int signum, + void (*handler)(int, siginfo_t*, void*)) { + struct sigaction sa = {}; + sa.sa_sigaction = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + TEST_PCHECK(sigaction(signum, &sa, nullptr) == 0); + MaybeSave(); +} + +// All of the following tests execute in a subprocess to ensure that each test +// is run in a separate process. This avoids cross-contamination of seccomp +// state between tests, and is necessary to ensure that test processes killed +// by SECCOMP_RET_KILL are single-threaded (since SECCOMP_RET_KILL only kills +// the offending thread, not the whole thread group). + +TEST(SeccompTest, RetKillCausesDeathBySIGSYS) { + pid_t const pid = fork(); + if (pid == 0) { + // Register a signal handler for SIGSYS that we don't expect to be invoked. + RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); + syscall(kFilteredSyscall); + TEST_CHECK_MSG(false, "Survived invocation of test syscall"); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS) + << "status " << status; +} + +TEST(SeccompTest, RetKillOnlyKillsOneThread) { + Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + + pid_t const pid = fork(); + if (pid == 0) { + // Register a signal handler for SIGSYS that we don't expect to be invoked. + RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); + // Pass CLONE_VFORK to block the original thread in the child process until + // the clone thread exits with SIGSYS. + // + // N.B. clone(2) is not officially async-signal-safe, but at minimum glibc's + // x86_64 implementation is safe. See glibc + // sysdeps/unix/sysv/linux/x86_64/clone.S. + clone( + +[](void* arg) { + syscall(kFilteredSyscall); // should kill the thread + _exit(1); // should be unreachable + return 2; // should be very unreachable, shut up the compiler + }, + stack.endptr(), + CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | + CLONE_VFORK, + nullptr); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +TEST(SeccompTest, RetTrapCausesSIGSYS) { + pid_t const pid = fork(); + if (pid == 0) { + constexpr uint16_t kTrapValue = 0xdead; + RegisterSignalHandler(SIGSYS, +[](int signo, siginfo_t* info, void*) { + // This is a signal handler, so we must stay async-signal-safe. + TEST_CHECK(info->si_signo == SIGSYS); + TEST_CHECK(info->si_code == SYS_SECCOMP); + TEST_CHECK(info->si_errno == kTrapValue); + TEST_CHECK(info->si_call_addr != nullptr); + TEST_CHECK(info->si_syscall == kFilteredSyscall); + TEST_CHECK(info->si_arch == AUDIT_ARCH_X86_64); + _exit(0); + }); + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRAP | kTrapValue); + syscall(kFilteredSyscall); + TEST_CHECK_MSG(false, "Survived invocation of test syscall"); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +constexpr uint64_t kVsyscallTimeEntry = 0xffffffffff600400; + +time_t vsyscall_time(time_t* t) { + return reinterpret_cast(kVsyscallTimeEntry)(t); +} + +TEST(SeccompTest, SeccompAppliesToVsyscall) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); + + pid_t const pid = fork(); + if (pid == 0) { + constexpr uint16_t kTrapValue = 0xdead; + RegisterSignalHandler(SIGSYS, +[](int signo, siginfo_t* info, void*) { + // This is a signal handler, so we must stay async-signal-safe. + TEST_CHECK(info->si_signo == SIGSYS); + TEST_CHECK(info->si_code == SYS_SECCOMP); + TEST_CHECK(info->si_errno == kTrapValue); + TEST_CHECK(info->si_call_addr != nullptr); + TEST_CHECK(info->si_syscall == SYS_time); + TEST_CHECK(info->si_arch == AUDIT_ARCH_X86_64); + _exit(0); + }); + ApplySeccompFilter(SYS_time, SECCOMP_RET_TRAP | kTrapValue); + vsyscall_time(nullptr); // Should result in death. + TEST_CHECK_MSG(false, "Survived invocation of test syscall"); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +TEST(SeccompTest, RetTraceWithoutPtracerReturnsENOSYS) { + pid_t const pid = fork(); + if (pid == 0) { + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRACE); + TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOSYS); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +TEST(SeccompTest, RetErrnoReturnsErrno) { + pid_t const pid = fork(); + if (pid == 0) { + // ENOTNAM: "Not a XENIX named type file" + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM); + TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOTNAM); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +TEST(SeccompTest, RetAllowAllowsSyscall) { + pid_t const pid = fork(); + if (pid == 0) { + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ALLOW); + TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOSYS); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +// This test will validate that TSYNC will apply to all threads. +TEST(SeccompTest, TsyncAppliesToAllThreads) { + Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + + // We don't want to apply this policy to other test runner threads, so fork. + const pid_t pid = fork(); + + if (pid == 0) { + // First check that we receive a ENOSYS before the policy is applied. + TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOSYS); + + // N.B. clone(2) is not officially async-signal-safe, but at minimum glibc's + // x86_64 implementation is safe. See glibc + // sysdeps/unix/sysv/linux/x86_64/clone.S. + clone( + +[](void* arg) { + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM, + SECCOMP_FILTER_FLAG_TSYNC); + return 0; + }, + stack.endptr(), + CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | + CLONE_VFORK, + nullptr); + + // Because we're using CLONE_VFORK this thread will be blocked until + // the second thread has released resources to our virtual memory, since + // we're not execing that will happen on _exit. + + // Now verify that the policy applied to this thread too. + TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOTNAM); + _exit(0); + } + + ASSERT_THAT(pid, SyscallSucceeds()); + int status = 0; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +// This test will validate that seccomp(2) rejects unsupported flags. +TEST(SeccompTest, SeccompRejectsUnknownFlags) { + constexpr uint32_t kInvalidFlag = 123; + ASSERT_THAT( + syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, kInvalidFlag, nullptr), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(SeccompTest, LeastPermissiveFilterReturnValueApplies) { + // This is RetKillCausesDeathBySIGSYS, plus extra filters before and after the + // one that causes the kill that should be ignored. + pid_t const pid = fork(); + if (pid == 0) { + RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); }); + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRACE); + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM); + syscall(kFilteredSyscall); + TEST_CHECK_MSG(false, "Survived invocation of test syscall"); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS) + << "status " << status; +} + +// Passed as argv[1] to cause the test binary to invoke kFilteredSyscall and +// exit. Not a real flag since flag parsing happens during initialization, +// which may create threads. +constexpr char kInvokeFilteredSyscallFlag[] = "--seccomp_test_child"; + +TEST(SeccompTest, FiltersPreservedAcrossForkAndExecve) { + ExecveArray const grandchild_argv( + {"/proc/self/exe", kInvokeFilteredSyscallFlag}); + + pid_t const pid = fork(); + if (pid == 0) { + ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); + pid_t const grandchild_pid = fork(); + if (grandchild_pid == 0) { + execve(grandchild_argv.get()[0], grandchild_argv.get(), + /* envp = */ nullptr); + TEST_PCHECK_MSG(false, "execve failed"); + } + int status; + TEST_PCHECK(waitpid(grandchild_pid, &status, 0) == grandchild_pid); + TEST_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS); + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status " << status; +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + if (argc >= 2 && + strcmp(argv[1], gvisor::testing::kInvokeFilteredSyscallFlag) == 0) { + syscall(gvisor::testing::kFilteredSyscall); + exit(0); + } + + gvisor::testing::TestInit(&argc, &argv); + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/select.cc b/test/syscalls/linux/select.cc new file mode 100644 index 000000000..6b6fa9217 --- /dev/null +++ b/test/syscalls/linux/select.cc @@ -0,0 +1,128 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/base_poll_test.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +class SelectTest : public BasePollTest { + protected: + void SetUp() override { BasePollTest::SetUp(); } + void TearDown() override { BasePollTest::TearDown(); } +}; + +// See that when there are no FD sets, select behaves like sleep. +TEST_F(SelectTest, NullFds) { + struct timeval timeout = absl::ToTimeval(absl::Milliseconds(10)); + ASSERT_THAT(select(0, nullptr, nullptr, nullptr, &timeout), + SyscallSucceeds()); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_usec, 0); + + timeout = absl::ToTimeval(absl::Milliseconds(10)); + ASSERT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), + SyscallSucceeds()); + EXPECT_EQ(timeout.tv_sec, 0); + EXPECT_EQ(timeout.tv_usec, 0); +} + +TEST_F(SelectTest, NegativeNfds) { + EXPECT_THAT(select(-1, nullptr, nullptr, nullptr, nullptr), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(select(-100000, nullptr, nullptr, nullptr, nullptr), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(select(INT_MIN, nullptr, nullptr, nullptr, nullptr), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(SelectTest, ClosedFds) { + fd_set read_set; + FD_ZERO(&read_set); + int fd; + ASSERT_THAT(fd = dup(1), SyscallSucceeds()); + ASSERT_THAT(close(fd), SyscallSucceeds()); + FD_SET(fd, &read_set); + struct timeval timeout = absl::ToTimeval(absl::Milliseconds(10)); + EXPECT_THAT(select(fd + 1, &read_set, nullptr, nullptr, &timeout), + SyscallFailsWithErrno(EBADF)); +} + +TEST_F(SelectTest, ZeroTimeout) { + struct timeval timeout = {}; + EXPECT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), + SyscallSucceeds()); + // Ignore timeout as its value is now undefined. +} + +// If random S/R interrupts the select, SIGALRM may be delivered before select +// restarts, causing the select to hang forever. +TEST_F(SelectTest, NoTimeout_NoRandomSave) { + // When there's no timeout, select may never return so set a timer. + SetTimer(absl::Milliseconds(100)); + // See that we get interrupted by the timer. + ASSERT_THAT(select(1, nullptr, nullptr, nullptr, nullptr), + SyscallFailsWithErrno(EINTR)); + EXPECT_TRUE(TimerFired()); +} + +TEST_F(SelectTest, InvalidTimeoutNegative) { + struct timeval timeout = absl::ToTimeval(absl::Microseconds(-1)); + EXPECT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), + SyscallFailsWithErrno(EINVAL)); + // Ignore timeout as its value is now undefined. +} + +// Verify that a signal interrupts select. +// +// If random S/R interrupts the select, SIGALRM may be delivered before select +// restarts, causing the select to hang forever. +TEST_F(SelectTest, InterruptedBySignal_NoRandomSave) { + absl::Duration duration(absl::Seconds(5)); + struct timeval timeout = absl::ToTimeval(duration); + SetTimer(absl::Milliseconds(100)); + ASSERT_FALSE(TimerFired()); + ASSERT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), + SyscallFailsWithErrno(EINTR)); + EXPECT_TRUE(TimerFired()); + // Ignore timeout as its value is now undefined. +} + +TEST_F(SelectTest, IgnoreBitsAboveNfds) { + // fd_set is a bit array with at least FD_SETSIZE bits. Test that bits + // corresponding to file descriptors above nfds are ignored. + fd_set read_set; + FD_ZERO(&read_set); + constexpr int kNfds = 1; + for (int fd = kNfds; fd < FD_SETSIZE; fd++) { + FD_SET(fd, &read_set); + } + // Pass a zero timeout so that select returns immediately. + struct timeval timeout = {}; + EXPECT_THAT(select(kNfds, &read_set, nullptr, nullptr, &timeout), + SyscallSucceedsWithValue(0)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc new file mode 100644 index 000000000..12e33732d --- /dev/null +++ b/test/syscalls/linux/semaphore.cc @@ -0,0 +1,438 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/macros.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" +#include "test/util/capability_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { +namespace { + +class AutoSem { + public: + explicit AutoSem(int id) : id_(id) {} + ~AutoSem() { + if (id_ >= 0) { + EXPECT_THAT(semctl(id_, 0, IPC_RMID), SyscallSucceeds()); + } + } + + int release() { + int old = id_; + id_ = -1; + return old; + } + + int get() { return id_; } + + private: + int id_ = -1; +}; + +TEST(SemaphoreTest, SemGet) { + // Test creation and lookup. + AutoSem sem(semget(1, 10, IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + EXPECT_THAT(semget(1, 10, IPC_CREAT), SyscallSucceedsWithValue(sem.get())); + EXPECT_THAT(semget(1, 9, IPC_CREAT), SyscallSucceedsWithValue(sem.get())); + + // Creation and lookup failure cases. + EXPECT_THAT(semget(1, 11, IPC_CREAT), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(semget(1, -1, IPC_CREAT), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(semget(1, 10, IPC_CREAT | IPC_EXCL), + SyscallFailsWithErrno(EEXIST)); + EXPECT_THAT(semget(2, 1, 0), SyscallFailsWithErrno(ENOENT)); + EXPECT_THAT(semget(2, 0, IPC_CREAT), SyscallFailsWithErrno(EINVAL)); + + // Private semaphores never conflict. + AutoSem sem2(semget(IPC_PRIVATE, 1, 0)); + AutoSem sem3(semget(IPC_PRIVATE, 1, 0)); + ASSERT_THAT(sem2.get(), SyscallSucceeds()); + EXPECT_NE(sem.get(), sem2.get()); + ASSERT_THAT(sem3.get(), SyscallSucceeds()); + EXPECT_NE(sem3.get(), sem2.get()); +} + +// Tests simple operations that shouldn't block in a single-thread. +TEST(SemaphoreTest, SemOpSingleNoBlock) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + struct sembuf buf = {}; + buf.sem_op = 1; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + + buf.sem_op = -1; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + + buf.sem_op = 0; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + + // Error cases with invalid values. + ASSERT_THAT(semop(sem.get() + 1, &buf, 1), SyscallFailsWithErrno(EINVAL)); + + buf.sem_num = 1; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EFBIG)); + + ASSERT_THAT(semop(sem.get(), nullptr, 0), SyscallFailsWithErrno(EINVAL)); +} + +// Tests multiple operations that shouldn't block in a single-thread. +TEST(SemaphoreTest, SemOpMultiNoBlock) { + AutoSem sem(semget(IPC_PRIVATE, 4, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + struct sembuf bufs[5] = {}; + bufs[0].sem_num = 0; + bufs[0].sem_op = 10; + bufs[0].sem_flg = 0; + + bufs[1].sem_num = 1; + bufs[1].sem_op = 2; + bufs[1].sem_flg = 0; + + bufs[2].sem_num = 2; + bufs[2].sem_op = 3; + bufs[2].sem_flg = 0; + + bufs[3].sem_num = 0; + bufs[3].sem_op = -5; + bufs[3].sem_flg = 0; + + bufs[4].sem_num = 2; + bufs[4].sem_op = 2; + bufs[4].sem_flg = 0; + + ASSERT_THAT(semop(sem.get(), bufs, ABSL_ARRAYSIZE(bufs)), SyscallSucceeds()); + + ASSERT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(5)); + ASSERT_THAT(semctl(sem.get(), 1, GETVAL), SyscallSucceedsWithValue(2)); + ASSERT_THAT(semctl(sem.get(), 2, GETVAL), SyscallSucceedsWithValue(5)); + ASSERT_THAT(semctl(sem.get(), 3, GETVAL), SyscallSucceedsWithValue(0)); + + for (auto& b : bufs) { + b.sem_op = -b.sem_op; + } + // 0 and 3 order must be reversed, otherwise it will block. + std::swap(bufs[0].sem_op, bufs[3].sem_op); + ASSERT_THAT(RetryEINTR(semop)(sem.get(), bufs, ABSL_ARRAYSIZE(bufs)), + SyscallSucceeds()); + + // All semaphores should be back to 0 now. + for (size_t i = 0; i < 4; ++i) { + ASSERT_THAT(semctl(sem.get(), i, GETVAL), SyscallSucceedsWithValue(0)); + } +} + +// Makes a best effort attempt to ensure that operation would block. +TEST(SemaphoreTest, SemOpBlock) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + std::atomic blocked = ATOMIC_VAR_INIT(1); + ScopedThread th([&sem, &blocked] { + absl::SleepFor(absl::Milliseconds(100)); + ASSERT_EQ(blocked.load(), 1); + + struct sembuf buf = {}; + buf.sem_op = 1; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + }); + + struct sembuf buf = {}; + buf.sem_op = -1; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + blocked.store(0); +} + +// Tests that IPC_NOWAIT returns with no wait. +TEST(SemaphoreTest, SemOpNoBlock) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + struct sembuf buf = {}; + buf.sem_flg = IPC_NOWAIT; + + buf.sem_op = -1; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EAGAIN)); + + buf.sem_op = 1; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + + buf.sem_op = 0; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EAGAIN)); +} + +// Test runs 2 threads, one signals the other waits the same number of times. +TEST(SemaphoreTest, SemOpSimple) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + constexpr size_t kLoops = 100; + ScopedThread th([&sem] { + struct sembuf buf = {}; + buf.sem_op = 1; + for (size_t i = 0; i < kLoops; i++) { + // Sleep to prevent making all increments in one shot without letting + // the waiter wait. + absl::SleepFor(absl::Milliseconds(1)); + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + } + }); + + struct sembuf buf = {}; + buf.sem_op = -1; + for (size_t i = 0; i < kLoops; i++) { + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + } +} + +// Tests that semaphore can be removed while there are waiters. +// NoRandomSave: Test relies on timing that random save throws off. +TEST(SemaphoreTest, SemOpRemoveWithWaiter_NoRandomSave) { + AutoSem sem(semget(IPC_PRIVATE, 2, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + ScopedThread th([&sem] { + absl::SleepFor(absl::Milliseconds(250)); + ASSERT_THAT(semctl(sem.release(), 0, IPC_RMID), SyscallSucceeds()); + }); + + // This must happen before IPC_RMID runs above. Otherwise it fails with EINVAL + // instead because the semaphire has already been removed. + struct sembuf buf = {}; + buf.sem_op = -1; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), + SyscallFailsWithErrno(EIDRM)); +} + +// Semaphore isn't fair. It will execute any waiter that can satisfy the +// request even if it gets in front of other waiters. +TEST(SemaphoreTest, SemOpBestFitExecution) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + ScopedThread th([&sem] { + struct sembuf buf = {}; + buf.sem_op = -2; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallFails()); + // Ensure that wait will only unblock when the semaphore is removed. On + // EINTR retry it may race with deletion and return EINVAL. + ASSERT_TRUE(errno == EIDRM || errno == EINVAL) << "errno=" << errno; + }); + + // Ensures that '-1' below will unblock even though '-10' above is waiting + // for the same semaphore. + for (size_t i = 0; i < 10; ++i) { + struct sembuf buf = {}; + buf.sem_op = 1; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + + absl::SleepFor(absl::Milliseconds(10)); + + buf.sem_op = -1; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + } + + ASSERT_THAT(semctl(sem.release(), 0, IPC_RMID), SyscallSucceeds()); +} + +// Executes random operations in multiple threads and verify correctness. +TEST(SemaphoreTest, SemOpRandom) { + // Don't do cooperative S/R tests because there are too many syscalls in + // this test, + const DisableSave ds; + + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + // Protects the seed below. + absl::Mutex mutex; + uint32_t seed = time(nullptr); + + int count = 0; // Tracks semaphore value. + bool done = false; // Tells waiters to stop after signal threads are done. + + // These threads will wait in a loop. + std::unique_ptr decs[5]; + for (auto& dec : decs) { + dec = absl::make_unique([&sem, &mutex, &count, &seed, &done] { + for (size_t i = 0; i < 500; ++i) { + int16_t val; + { + absl::MutexLock l(&mutex); + if (done) { + return; + } + val = (rand_r(&seed) % 10 + 1); // Rand between 1 and 10. + count -= val; + } + struct sembuf buf = {}; + buf.sem_op = -val; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + absl::SleepFor(absl::Milliseconds(val * 2)); + } + }); + } + + // These threads will wait for zero in a loop. + std::unique_ptr zeros[5]; + for (auto& zero : zeros) { + zero = absl::make_unique([&sem, &mutex, &done] { + for (size_t i = 0; i < 500; ++i) { + { + absl::MutexLock l(&mutex); + if (done) { + return; + } + } + struct sembuf buf = {}; + buf.sem_op = 0; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + absl::SleepFor(absl::Milliseconds(10)); + } + }); + } + + // These threads will signal in a loop. + std::unique_ptr incs[5]; + for (auto& inc : incs) { + inc = absl::make_unique([&sem, &mutex, &count, &seed] { + for (size_t i = 0; i < 500; ++i) { + int16_t val; + { + absl::MutexLock l(&mutex); + val = (rand_r(&seed) % 10 + 1); // Rand between 1 and 10. + count += val; + } + struct sembuf buf = {}; + buf.sem_op = val; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + absl::SleepFor(absl::Milliseconds(val * 2)); + } + }); + } + + // First wait for signal threads to be done. + for (auto& inc : incs) { + inc->Join(); + } + + // Now there could be waiters blocked (remember operations are random). + // Notify waiters that we're done and signal semaphore just the right amount. + { + absl::MutexLock l(&mutex); + done = true; + struct sembuf buf = {}; + buf.sem_op = -count; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + } + + // Now all waiters should unblock and exit. + for (auto& dec : decs) { + dec->Join(); + } + for (auto& zero : zeros) { + zero->Join(); + } +} + +TEST(SemaphoreTest, SemOpNamespace) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + AutoSem sem(semget(123, 1, 0600 | IPC_CREAT | IPC_EXCL)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + ScopedThread([]() { + EXPECT_THAT(unshare(CLONE_NEWIPC), SyscallSucceeds()); + AutoSem sem(semget(123, 1, 0600 | IPC_CREAT | IPC_EXCL)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + }); +} + +TEST(SemaphoreTest, SemCtlVal) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + // Semaphore must start with 0. + EXPECT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(0)); + + // Increase value and ensure waiters are woken up. + ScopedThread th([&sem] { + struct sembuf buf = {}; + buf.sem_op = -10; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + }); + + ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 9), SyscallSucceeds()); + EXPECT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(9)); + + ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 20), SyscallSucceeds()); + const int value = semctl(sem.get(), 0, GETVAL); + // 10 or 20 because it could have raced with waiter above. + EXPECT_TRUE(value == 10 || value == 20) << "value=" << value; + th.Join(); + + // Set it back to 0 and ensure that waiters are woken up. + ScopedThread thZero([&sem] { + struct sembuf buf = {}; + buf.sem_op = 0; + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + }); + ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 0), SyscallSucceeds()); + EXPECT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(0)); + thZero.Join(); +} + +TEST(SemaphoreTest, SemIpcSet) { + // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + struct semid_ds semid = {}; + semid.sem_perm.uid = getuid(); + semid.sem_perm.gid = getgid(); + + // Make semaphore readonly and check that signal fails. + semid.sem_perm.mode = 0400; + EXPECT_THAT(semctl(sem.get(), 0, IPC_SET, &semid), SyscallSucceeds()); + struct sembuf buf = {}; + buf.sem_op = 1; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EACCES)); + + // Make semaphore writeonly and check that wait for zero fails. + semid.sem_perm.mode = 0200; + EXPECT_THAT(semctl(sem.get(), 0, IPC_SET, &semid), SyscallSucceeds()); + buf.sem_op = 0; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EACCES)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sendfile.cc b/test/syscalls/linux/sendfile.cc new file mode 100644 index 000000000..92b7b9478 --- /dev/null +++ b/test/syscalls/linux/sendfile.cc @@ -0,0 +1,409 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(SendFileTest, SendZeroBytes) { + // Create temp files. + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as write only. + const FileDescriptor outf = + ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Send data and verify that sendfile returns the correct value. + EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, 0), + SyscallSucceedsWithValue(0)); +} + +TEST(SendFileTest, SendTrivially) { + // Create temp files. + constexpr char kData[] = "To be, or not to be, that is the question:"; + constexpr int kDataSize = sizeof(kData) - 1; + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as write only. + FileDescriptor outf; + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Send data and verify that sendfile returns the correct value. + int bytes_sent; + EXPECT_THAT(bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize), + SyscallSucceedsWithValue(kDataSize)); + + // Close outf to avoid leak. + outf.reset(); + + // Open the output file as read only. + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + + // Verify that the output file has the correct data. + char actual[kDataSize]; + ASSERT_THAT(read(outf.get(), &actual, bytes_sent), + SyscallSucceedsWithValue(kDataSize)); + EXPECT_EQ(kData, absl::string_view(actual, bytes_sent)); +} + +TEST(SendFileTest, SendTriviallyWithBothFilesReadWrite) { + // Create temp files. + constexpr char kData[] = "Whether 'tis nobler in the mind to suffer"; + constexpr int kDataSize = sizeof(kData) - 1; + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as readwrite. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); + + // Open the output file as readwrite. + FileDescriptor outf; + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR)); + + // Send data and verify that sendfile returns the correct value. + int bytes_sent; + EXPECT_THAT(bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize), + SyscallSucceedsWithValue(kDataSize)); + + // Close outf to avoid leak. + outf.reset(); + + // Open the output file as read only. + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + + // Verify that the output file has the correct data. + char actual[kDataSize]; + ASSERT_THAT(read(outf.get(), &actual, bytes_sent), + SyscallSucceedsWithValue(kDataSize)); + EXPECT_EQ(kData, absl::string_view(actual, bytes_sent)); +} + +TEST(SendFileTest, SendAndUpdateFileOffset) { + // Create temp files. + // Test input std::string length must be > 2 AND even. + constexpr char kData[] = "The slings and arrows of outrageous fortune,"; + constexpr int kDataSize = sizeof(kData) - 1; + constexpr int kHalfDataSize = kDataSize / 2; + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as write only. + FileDescriptor outf; + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Send data and verify that sendfile returns the correct value. + int bytes_sent; + EXPECT_THAT( + bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize), + SyscallSucceedsWithValue(kHalfDataSize)); + + // Close outf to avoid leak. + outf.reset(); + + // Open the output file as read only. + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + + // Verify that the output file has the correct data. + char actual[kHalfDataSize]; + ASSERT_THAT(read(outf.get(), &actual, bytes_sent), + SyscallSucceedsWithValue(kHalfDataSize)); + EXPECT_EQ(absl::string_view(kData, kHalfDataSize), + absl::string_view(actual, bytes_sent)); + + // Verify that the input file offset has been updated + ASSERT_THAT(read(inf.get(), &actual, kDataSize - bytes_sent), + SyscallSucceedsWithValue(kHalfDataSize)); + EXPECT_EQ( + absl::string_view(kData + kDataSize - bytes_sent, kDataSize - bytes_sent), + absl::string_view(actual, kHalfDataSize)); +} + +TEST(SendFileTest, SendAndUpdateFileOffsetFromNonzeroStartingPoint) { + // Create temp files. + // Test input std::string length must be > 2 AND divisible by 4. + constexpr char kData[] = "The slings and arrows of outrageous fortune,"; + constexpr int kDataSize = sizeof(kData) - 1; + constexpr int kHalfDataSize = kDataSize / 2; + constexpr int kQuarterDataSize = kHalfDataSize / 2; + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as write only. + FileDescriptor outf; + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Read a quarter of the data from the infile which should update the file + // offset, we don't actually care about the data so it goes into the garbage. + char garbage[kQuarterDataSize]; + ASSERT_THAT(read(inf.get(), &garbage, kQuarterDataSize), + SyscallSucceedsWithValue(kQuarterDataSize)); + + // Send data and verify that sendfile returns the correct value. + int bytes_sent; + EXPECT_THAT( + bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize), + SyscallSucceedsWithValue(kHalfDataSize)); + + // Close out_fd to avoid leak. + outf.reset(); + + // Open the output file as read only. + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + + // Verify that the output file has the correct data. + char actual[kHalfDataSize]; + ASSERT_THAT(read(outf.get(), &actual, bytes_sent), + SyscallSucceedsWithValue(kHalfDataSize)); + EXPECT_EQ(absl::string_view(kData + kQuarterDataSize, kHalfDataSize), + absl::string_view(actual, bytes_sent)); + + // Verify that the input file offset has been updated + ASSERT_THAT(read(inf.get(), &actual, kQuarterDataSize), + SyscallSucceedsWithValue(kQuarterDataSize)); + + EXPECT_EQ( + absl::string_view(kData + kDataSize - kQuarterDataSize, kQuarterDataSize), + absl::string_view(actual, kQuarterDataSize)); +} + +TEST(SendFileTest, SendAndUpdateGivenOffset) { + // Create temp files. + // Test input std::string length must be >= 4 AND divisible by 4. + constexpr char kData[] = "Or to take Arms against a Sea of troubles,"; + constexpr int kDataSize = sizeof(kData) + 1; + constexpr int kHalfDataSize = kDataSize / 2; + constexpr int kQuarterDataSize = kHalfDataSize / 2; + constexpr int kThreeFourthsDataSize = 3 * kDataSize / 4; + + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as write only. + FileDescriptor outf; + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Create offset for sending. + off_t offset = kQuarterDataSize; + + // Send data and verify that sendfile returns the correct value. + int bytes_sent; + EXPECT_THAT( + bytes_sent = sendfile(outf.get(), inf.get(), &offset, kHalfDataSize), + SyscallSucceedsWithValue(kHalfDataSize)); + + // Close out_fd to avoid leak. + outf.reset(); + + // Open the output file as read only. + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + + // Verify that the output file has the correct data. + char actual[kHalfDataSize]; + ASSERT_THAT(read(outf.get(), &actual, bytes_sent), + SyscallSucceedsWithValue(kHalfDataSize)); + EXPECT_EQ(absl::string_view(kData + kQuarterDataSize, kHalfDataSize), + absl::string_view(actual, bytes_sent)); + + // Verify that the input file offset has NOT been updated. + ASSERT_THAT(read(inf.get(), &actual, kHalfDataSize), + SyscallSucceedsWithValue(kHalfDataSize)); + EXPECT_EQ(absl::string_view(kData, kHalfDataSize), + absl::string_view(actual, kHalfDataSize)); + + // Verify that the offset pointer has been updated. + EXPECT_EQ(offset, kThreeFourthsDataSize); +} + +TEST(SendFileTest, DoNotSendfileIfOutfileIsAppendOnly) { + // Create temp files. + constexpr char kData[] = "And by opposing end them: to die, to sleep"; + constexpr int kDataSize = sizeof(kData) - 1; + + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as append only. + const FileDescriptor outf = + ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_APPEND)); + + // Send data and verify that sendfile returns the correct errno. + EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize), + SyscallFailsWithErrno(EBADF)); +} + +TEST(SendFileTest, DoNotSendfileIfOutfileIsNotWritable) { + // Create temp files. + constexpr char kData[] = "No more; and by a sleep, to say we end"; + constexpr int kDataSize = sizeof(kData) - 1; + + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as read only. + const FileDescriptor outf = + ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + + // Send data and verify that sendfile returns the correct errno. + EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize), + SyscallFailsWithErrno(EBADF)); +} + +TEST(SendFileTest, DoNotSendfileIfInfileIsNotReadable) { + // Create temp files. + constexpr char kData[] = "the heart-ache, and the thousand natural shocks"; + constexpr int kDataSize = sizeof(kData) - 1; + + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as write only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_WRONLY)); + + // Open the output file as write only. + const FileDescriptor outf = + ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Send data and verify that sendfile returns the correct errno. + EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize), + SyscallFailsWithErrno(EBADF)); +} + +TEST(SendFileTest, DoNotSendANegativeNumberOfBytes) { + // Create temp files. + constexpr char kData[] = "that Flesh is heir to? 'Tis a consummation"; + + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as write only. + const FileDescriptor outf = + ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Send data and verify that sendfile returns the correct errno. + EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, -1), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(SendFileTest, SendTheCorrectNumberOfBytesEvenIfWeTryToSendTooManyBytes) { + // Create temp files. + constexpr char kData[] = "devoutly to be wished. To die, to sleep,"; + constexpr int kDataSize = sizeof(kData) - 1; + + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + // Open the output file as write only. + FileDescriptor outf; + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Send data and verify that sendfile returns the correct value. + int bytes_sent; + EXPECT_THAT( + bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize + 100), + SyscallSucceedsWithValue(kDataSize)); + + // Close outf to avoid leak. + outf.reset(); + + // Open the output file as read only. + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + + // Verify that the output file has the correct data. + char actual[kDataSize]; + ASSERT_THAT(read(outf.get(), &actual, bytes_sent), + SyscallSucceedsWithValue(kDataSize)); + EXPECT_EQ(kData, absl::string_view(actual, bytes_sent)); +} + +TEST(SendFileTest, SendToNotARegularFile) { + // Make temp input directory and open as read only. + const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY)); + + // Make temp output file and open as write only. + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor outf = + ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Receive an error since a directory is not a regular file. + EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, 0), + SyscallFailsWithErrno(EINVAL)); +} +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sendfile_socket.cc b/test/syscalls/linux/sendfile_socket.cc new file mode 100644 index 000000000..7010dc211 --- /dev/null +++ b/test/syscalls/linux/sendfile_socket.cc @@ -0,0 +1,156 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { +namespace { + +// Sends large file to exercise the path that read and writes data multiple +// times, esp. when more data is read than can be written. +TEST(SendFileTest, SendMultiple) { + std::vector data(5 * 1024 * 1024); + RandomizeBuffer(data.data(), data.size()); + + // Create temp files. + const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::string_view(data.data(), data.size()), + TempPath::kDefaultFileMode)); + const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + // Use a socket for target file to make the write window small. + const FileDescriptor server(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_THAT(server.get(), SyscallSucceeds()); + + struct sockaddr_in server_addr = {}; + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + ASSERT_THAT( + bind(server.get(), reinterpret_cast(&server_addr), + sizeof(server_addr)), + SyscallSucceeds()); + ASSERT_THAT(listen(server.get(), 1), SyscallSucceeds()); + + // Thread that reads data from socket and dumps to a file. + ScopedThread th([&server, &out_file, &server_addr] { + socklen_t addrlen = sizeof(server_addr); + const FileDescriptor fd(RetryEINTR(accept)( + server.get(), reinterpret_cast(&server_addr), + &addrlen)); + ASSERT_THAT(fd.get(), SyscallSucceeds()); + + FileDescriptor outf = + ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); + + // Read until socket is closed. + char buf[10240]; + for (int cnt = 0;; cnt++) { + int r = RetryEINTR(read)(fd.get(), buf, sizeof(buf)); + // We cannot afford to save on every read() call. + if (cnt % 1000 == 0) { + ASSERT_THAT(r, SyscallSucceeds()); + } else { + const DisableSave ds; + ASSERT_THAT(r, SyscallSucceeds()); + } + if (r == 0) { + // EOF + break; + } + int w = RetryEINTR(write)(outf.get(), buf, r); + // We cannot afford to save on every write() call. + if (cnt % 1010 == 0) { + ASSERT_THAT(w, SyscallSucceedsWithValue(r)); + } else { + const DisableSave ds; + ASSERT_THAT(w, SyscallSucceedsWithValue(r)); + } + } + }); + + // Open the input file as read only. + const FileDescriptor inf = + ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); + + FileDescriptor outf(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_THAT(outf.get(), SyscallSucceeds()); + + // Get the port bound by the listening socket. + socklen_t addrlen = sizeof(server_addr); + ASSERT_THAT(getsockname(server.get(), + reinterpret_cast(&server_addr), &addrlen), + SyscallSucceeds()); + + struct sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = server_addr.sin_port; + LOG(INFO) << "Connecting on port=" << server_addr.sin_port; + ASSERT_THAT( + RetryEINTR(connect)( + outf.get(), reinterpret_cast(&addr), sizeof(addr)), + SyscallSucceeds()); + + int cnt = 0; + for (size_t sent = 0; sent < data.size(); cnt++) { + const size_t remain = data.size() - sent; + LOG(INFO) << "sendfile, size=" << data.size() << ", sent=" << sent + << ", remain=" << remain; + + // Send data and verify that sendfile returns the correct value. + int res = sendfile(outf.get(), inf.get(), nullptr, remain); + // We cannot afford to save on every sendfile() call. + if (cnt % 120 == 0) { + MaybeSave(); + } + if (res == 0) { + // EOF + break; + } + if (res > 0) { + sent += res; + } else { + ASSERT_TRUE(errno == EINTR || errno == EAGAIN) << "errno=" << errno; + } + } + + // Close socket to stop thread. + outf.reset(); + th.Join(); + + // Verify that the output file has the correct data. + outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); + std::vector actual(data.size(), '\0'); + ASSERT_THAT(RetryEINTR(read)(outf.get(), actual.data(), actual.size()), + SyscallSucceedsWithValue(actual.size())); + ASSERT_EQ(memcmp(data.data(), actual.data(), data.size()), 0); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/shm.cc b/test/syscalls/linux/shm.cc new file mode 100644 index 000000000..9f57476c9 --- /dev/null +++ b/test/syscalls/linux/shm.cc @@ -0,0 +1,445 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include +#include +#include +#include + +#include "absl/time/clock.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +using ::testing::_; + +const uint64_t kAllocSize = kPageSize * 128ULL; + +PosixErrorOr Shmget(key_t key, size_t size, int shmflg) { + int id = shmget(key, size, shmflg); + if (id == -1) { + return PosixError(errno, "shmget() failed"); + } + return id; +} + +PosixErrorOr Shmat(int shmid, const void* shmaddr, int shmflg) { + const intptr_t addr = + reinterpret_cast(shmat(shmid, shmaddr, shmflg)); + if (addr == -1) { + return PosixError(errno, "shmat() failed"); + } + return reinterpret_cast(addr); +} + +PosixError Shmdt(const char* shmaddr) { + const int ret = shmdt(shmaddr); + if (ret == -1) { + return PosixError(errno, "shmdt() failed"); + } + return NoError(); +} + +template +PosixErrorOr Shmctl(int shmid, int cmd, T* buf) { + int ret = shmctl(shmid, cmd, reinterpret_cast(buf)); + if (ret == -1) { + return PosixError(errno, "shmctl() failed"); + } + return ret; +} + +TEST(ShmTest, AttachDetach) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + struct shmid_ds attr; + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + EXPECT_EQ(attr.shm_segsz, kAllocSize); + EXPECT_EQ(attr.shm_nattch, 0); + + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + EXPECT_EQ(attr.shm_nattch, 1); + + const char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + EXPECT_EQ(attr.shm_nattch, 2); + + ASSERT_NO_ERRNO(Shmdt(addr)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + EXPECT_EQ(attr.shm_nattch, 1); + + ASSERT_NO_ERRNO(Shmdt(addr2)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + EXPECT_EQ(attr.shm_nattch, 0); +} + +TEST(ShmTest, LookupByKey) { + const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const key_t key = ftok(keyfile.path().c_str(), 1); + const int id = + ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); + const int id2 = ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, 0777)); + EXPECT_EQ(id, id2); +} + +TEST(ShmTest, DetachedSegmentsPersist) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + addr[0] = 'x'; + ASSERT_NO_ERRNO(Shmdt(addr)); + + // We should be able to re-attach to the same segment and get our data back. + addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + EXPECT_EQ(addr[0], 'x'); + ASSERT_NO_ERRNO(Shmdt(addr)); +} + +TEST(ShmTest, MultipleDetachFails) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + ASSERT_NO_ERRNO(Shmdt(addr)); + EXPECT_THAT(Shmdt(addr), PosixErrorIs(EINVAL, _)); +} + +TEST(ShmTest, IpcStat) { + const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const key_t key = ftok(keyfile.path().c_str(), 1); + + const time_t start = time(nullptr); + + const int id = + ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); + + const uid_t uid = getuid(); + const gid_t gid = getgid(); + const pid_t pid = getpid(); + + struct shmid_ds attr; + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + + EXPECT_EQ(attr.shm_perm.__key, key); + EXPECT_EQ(attr.shm_perm.uid, uid); + EXPECT_EQ(attr.shm_perm.gid, gid); + EXPECT_EQ(attr.shm_perm.cuid, uid); + EXPECT_EQ(attr.shm_perm.cgid, gid); + EXPECT_EQ(attr.shm_perm.mode, 0777); + + EXPECT_EQ(attr.shm_segsz, kAllocSize); + + EXPECT_EQ(attr.shm_atime, 0); + EXPECT_EQ(attr.shm_dtime, 0); + + // Change time is set on creation. + EXPECT_GE(attr.shm_ctime, start); + + EXPECT_EQ(attr.shm_cpid, pid); + EXPECT_EQ(attr.shm_lpid, 0); + + EXPECT_EQ(attr.shm_nattch, 0); + + // The timestamps only have a resolution of seconds; slow down so we actually + // see the timestamps change. + absl::SleepFor(absl::Seconds(1)); + const time_t pre_attach = time(nullptr); + + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + + EXPECT_GE(attr.shm_atime, pre_attach); + EXPECT_EQ(attr.shm_dtime, 0); + EXPECT_LT(attr.shm_ctime, pre_attach); + EXPECT_EQ(attr.shm_lpid, pid); + EXPECT_EQ(attr.shm_nattch, 1); + + absl::SleepFor(absl::Seconds(1)); + const time_t pre_detach = time(nullptr); + + ASSERT_NO_ERRNO(Shmdt(addr)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + + EXPECT_LT(attr.shm_atime, pre_detach); + EXPECT_GE(attr.shm_dtime, pre_detach); + EXPECT_LT(attr.shm_ctime, pre_detach); + EXPECT_EQ(attr.shm_lpid, pid); + EXPECT_EQ(attr.shm_nattch, 0); +} + +TEST(ShmTest, ShmStat) { + // This test relies on the segment we create to be the first one on the + // system, causing it to occupy slot 1. We can't reasonably expect this on a + // general Linux host. + SKIP_IF(!IsRunningOnGvisor()); + + ASSERT_NO_ERRNO(Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + struct shmid_ds attr; + ASSERT_NO_ERRNO(Shmctl(1, SHM_STAT, &attr)); + // This does the same thing as IPC_STAT, so only test that the syscall + // succeeds here. +} + +TEST(ShmTest, IpcInfo) { + struct shminfo info; + ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); + + EXPECT_EQ(info.shmmin, 1); // This is always 1, according to the man page. + EXPECT_GT(info.shmmax, info.shmmin); + EXPECT_GT(info.shmmni, 0); + EXPECT_GT(info.shmseg, 0); + EXPECT_GT(info.shmall, 0); +} + +TEST(ShmTest, ShmInfo) { + struct shm_info info; + + // We generally can't know what other processes on a linux machine + // does with shared memory segments, so we can't test specific + // numbers on Linux. When running under gvisor, we're guaranteed to + // be the only ones using shm, so we can easily verify machine-wide + // numbers. + if (IsRunningOnGvisor()) { + ASSERT_NO_ERRNO(Shmctl(0, SHM_INFO, &info)); + EXPECT_EQ(info.used_ids, 0); + EXPECT_EQ(info.shm_tot, 0); + EXPECT_EQ(info.shm_rss, 0); + EXPECT_EQ(info.shm_swp, 0); + } + + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + + ASSERT_NO_ERRNO(Shmctl(1, SHM_INFO, &info)); + + if (IsRunningOnGvisor()) { + ASSERT_NO_ERRNO(Shmctl(id, SHM_INFO, &info)); + EXPECT_EQ(info.used_ids, 1); + EXPECT_EQ(info.shm_tot, kAllocSize / kPageSize); + EXPECT_EQ(info.shm_rss, kAllocSize / kPageSize); + EXPECT_EQ(info.shm_swp, 0); // Gvisor currently never swaps. + } + + ASSERT_NO_ERRNO(Shmdt(addr)); +} + +TEST(ShmTest, ShmCtlSet) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + + struct shmid_ds attr; + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + ASSERT_EQ(attr.shm_perm.mode, 0777); + + attr.shm_perm.mode = 0766; + ASSERT_NO_ERRNO(Shmctl(id, IPC_SET, &attr)); + + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + ASSERT_EQ(attr.shm_perm.mode, 0766); + + ASSERT_NO_ERRNO(Shmdt(addr)); +} + +TEST(ShmTest, RemovedSegmentsAreMarkedDeleted) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_RMID, nullptr)); + struct shmid_ds attr; + ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); + EXPECT_NE(attr.shm_perm.mode & SHM_DEST, 0); + ASSERT_NO_ERRNO(Shmdt(addr)); +} + +TEST(ShmTest, RemovedSegmentsAreDestroyed) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + + const uint64_t alloc_pages = kAllocSize / kPageSize; + + struct shm_info info; + ASSERT_NO_ERRNO(Shmctl(1, SHM_INFO, &info)); + const uint64_t before = info.shm_tot; + + ASSERT_NO_ERRNO(Shmctl(id, IPC_RMID, nullptr)); + ASSERT_NO_ERRNO(Shmdt(addr)); + + ASSERT_NO_ERRNO(Shmctl(1, SHM_INFO, &info)); + const uint64_t after = info.shm_tot; + EXPECT_EQ(after, before - alloc_pages); +} + +TEST(ShmTest, AllowsAttachToRemovedSegmentWithRefs) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_RMID, nullptr)); + const char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + ASSERT_NO_ERRNO(Shmdt(addr)); + ASSERT_NO_ERRNO(Shmdt(addr2)); +} + +TEST(ShmTest, RemovedSegmentsAreNotDiscoverable) { + const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const key_t key = ftok(keyfile.path().c_str(), 1); + const int id = + ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); + ASSERT_NO_ERRNO(Shmctl(id, IPC_RMID, nullptr)); + EXPECT_THAT(Shmget(key, kAllocSize, 0777), PosixErrorIs(ENOENT, _)); +} + +TEST(ShmDeathTest, ReadonlySegment) { + SetupGvisorDeathTest(); + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, SHM_RDONLY)); + // Reading succeeds. + static_cast(addr[0]); + // Writing fails. + EXPECT_EXIT(addr[0] = 'x', ::testing::KilledBySignal(SIGSEGV), ""); +} + +TEST(ShmDeathTest, SegmentNotAccessibleAfterDetach) { + // This test is susceptible to races with concurrent mmaps running in parallel + // gtest threads since the test relies on the address freed during a shm + // segment destruction to remain unused. We run the test body in a forked + // child to guarantee a single-threaded context to avoid this. + + SetupGvisorDeathTest(); + + const auto rest = [&] { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + addr[0] = 'x'; + ASSERT_NO_ERRNO(Shmdt(addr)); + + // This access should cause a SIGSEGV. + addr[0] = 'x'; + }; + + EXPECT_THAT(InForkedProcess(rest), + IsPosixErrorOkAndHolds(W_EXITCODE(0, SIGSEGV))); +} + +TEST(ShmTest, RequestingSegmentSmallerThanSHMMINFails) { + struct shminfo info; + ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); + const uint64_t size = info.shmmin - 1; + EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), + PosixErrorIs(EINVAL, _)); +} + +TEST(ShmTest, RequestingSegmentLargerThanSHMMAXFails) { + struct shminfo info; + ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); + const uint64_t size = info.shmmax + kPageSize; + EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), + PosixErrorIs(EINVAL, _)); +} + +TEST(ShmTest, RequestingUnalignedSizeSucceeds) { + EXPECT_NO_ERRNO(Shmget(IPC_PRIVATE, 4097, IPC_CREAT | 0777)); +} + +TEST(ShmTest, RequestingDuplicateCreationFails) { + const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const key_t key = ftok(keyfile.path().c_str(), 1); + ASSERT_NO_ERRNO_AND_VALUE( + Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777)); + EXPECT_THAT(Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777), + PosixErrorIs(EEXIST, _)); +} + +TEST(ShmTest, SegmentsSizeFixedOnCreation) { + const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const key_t key = ftok(keyfile.path().c_str(), 1); + + // Base segment. + const int id = + ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); + + // Ask for the same segment at half size. This succeeds. + const int id2 = ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize / 2, 0777)); + + // Ask for the same segment at double size. + EXPECT_THAT(Shmget(key, kAllocSize * 2, 0777), PosixErrorIs(EINVAL, _)); + + char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id2, nullptr, 0)); + + // We have 2 different maps... + EXPECT_NE(addr, addr2); + + // ... And both maps are kAllocSize bytes; despite asking for a half-sized + // segment for the second map. + addr[kAllocSize - 1] = 'x'; + addr2[kAllocSize - 1] = 'x'; + + ASSERT_NO_ERRNO(Shmdt(addr)); + ASSERT_NO_ERRNO(Shmdt(addr2)); +} + +TEST(ShmTest, PartialUnmap) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + EXPECT_THAT(munmap(addr + (kAllocSize / 4), kAllocSize / 2), + SyscallSucceeds()); + ASSERT_NO_ERRNO(Shmdt(addr)); +} + +// Check that sentry does not panic when asked for a zero-length private shm +// segment. +TEST(ShmTest, GracefullyFailOnZeroLenSegmentCreation) { + EXPECT_THAT(Shmget(IPC_PRIVATE, 0, 0), PosixErrorIs(EINVAL, _)); +} + +TEST(ShmTest, NoDestructionOfAttachedSegmentWithMultipleRmid) { + const int id = ASSERT_NO_ERRNO_AND_VALUE( + Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); + char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); + + // There should be 2 refs to the segment from the 2 attachments, and a single + // self-reference. Mark the segment as destroyed more than 3 times through + // shmctl(RMID). If there's a bug with the ref counting, this should cause the + // count to drop to zero. + for (int i = 0; i < 6; ++i) { + ASSERT_NO_ERRNO(Shmctl(id, IPC_RMID, nullptr)); + } + + // Segment should remain accessible. + addr[0] = 'x'; + ASSERT_NO_ERRNO(Shmdt(addr)); + + // Segment should remain accessible even after one of the two attachments are + // detached. + addr2[0] = 'x'; + ASSERT_NO_ERRNO(Shmdt(addr2)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sigaction.cc b/test/syscalls/linux/sigaction.cc new file mode 100644 index 000000000..cdd2dbf31 --- /dev/null +++ b/test/syscalls/linux/sigaction.cc @@ -0,0 +1,70 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(SigactionTest, GetLessThanOrEqualToZeroFails) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + ASSERT_THAT(sigaction(-1, NULL, &act), SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT(sigaction(0, NULL, &act), SyscallFailsWithErrno(EINVAL)); +} + +TEST(SigactionTest, SetLessThanOrEqualToZeroFails) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + ASSERT_THAT(sigaction(0, &act, NULL), SyscallFailsWithErrno(EINVAL)); + ASSERT_THAT(sigaction(0, &act, NULL), SyscallFailsWithErrno(EINVAL)); +} + +TEST(SigactionTest, GetGreaterThanMaxFails) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + ASSERT_THAT(sigaction(SIGRTMAX + 1, NULL, &act), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(SigactionTest, SetGreaterThanMaxFails) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + ASSERT_THAT(sigaction(SIGRTMAX + 1, &act, NULL), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(SigactionTest, SetSigkillFails) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + ASSERT_THAT(sigaction(SIGKILL, NULL, &act), SyscallSucceeds()); + ASSERT_THAT(sigaction(SIGKILL, &act, NULL), SyscallFailsWithErrno(EINVAL)); +} + +TEST(SigactionTest, SetSigstopFails) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + ASSERT_THAT(sigaction(SIGSTOP, NULL, &act), SyscallSucceeds()); + ASSERT_THAT(sigaction(SIGSTOP, &act, NULL), SyscallFailsWithErrno(EINVAL)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sigaltstack.cc b/test/syscalls/linux/sigaltstack.cc new file mode 100644 index 000000000..fa991545c --- /dev/null +++ b/test/syscalls/linux/sigaltstack.cc @@ -0,0 +1,274 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/util/cleanup.h" +#include "test/util/fs_util.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +PosixErrorOr ScopedSigaltstack(stack_t const& stack) { + stack_t old_stack; + int rc = sigaltstack(&stack, &old_stack); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "sigaltstack failed"); + } + return Cleanup([old_stack] { + EXPECT_THAT(sigaltstack(&old_stack, nullptr), SyscallSucceeds()); + }); +} + +volatile bool got_signal = false; +volatile int sigaltstack_errno = 0; +volatile int ss_flags = 0; + +void sigaltstack_handler(int sig, siginfo_t* siginfo, void* arg) { + got_signal = true; + + stack_t stack; + int ret = sigaltstack(nullptr, &stack); + MaybeSave(); + if (ret < 0) { + sigaltstack_errno = errno; + return; + } + ss_flags = stack.ss_flags; +} + +TEST(SigaltstackTest, Success) { + std::vector stack_mem(SIGSTKSZ); + stack_t stack = {}; + stack.ss_sp = stack_mem.data(); + stack.ss_size = stack_mem.size(); + auto const cleanup_sigstack = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); + + struct sigaction sa = {}; + sa.sa_sigaction = sigaltstack_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + auto const cleanup_sa = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa)); + + // Send signal to this thread, as sigaltstack is per-thread. + EXPECT_THAT(tgkill(getpid(), gettid(), SIGUSR1), SyscallSucceeds()); + + EXPECT_TRUE(got_signal); + EXPECT_EQ(sigaltstack_errno, 0); + EXPECT_NE(0, ss_flags & SS_ONSTACK); +} + +TEST(SigaltstackTest, ResetByExecve) { + std::vector stack_mem(SIGSTKSZ); + stack_t stack = {}; + stack.ss_sp = stack_mem.data(); + stack.ss_size = stack_mem.size(); + auto const cleanup_sigstack = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); + + std::string full_path; + char* test_src = getenv("TEST_SRCDIR"); + if (test_src) { + full_path = JoinPath(test_src, "../../linux/sigaltstack_check"); + } + ASSERT_FALSE(full_path.empty()); + + pid_t child_pid = -1; + int execve_errno = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(full_path, {"sigaltstack_check"}, {}, nullptr, &child_pid, + &execve_errno)); + + ASSERT_GT(child_pid, 0); + ASSERT_EQ(execve_errno, 0); + + int status = 0; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0); +} + +volatile bool badhandler_on_sigaltstack = true; // Set by the handler. +char* volatile badhandler_low_water_mark = nullptr; // Set by the handler. +volatile uint8_t badhandler_recursive_faults = 0; // Consumed by the handler. + +void badhandler(int sig, siginfo_t* siginfo, void* arg) { + char stack_var = 0; + char* current_ss = &stack_var; + + stack_t stack; + int ret = sigaltstack(nullptr, &stack); + if (ret < 0 || (stack.ss_flags & SS_ONSTACK) != SS_ONSTACK) { + // We should always be marked as being on the stack. Don't allow this to hit + // the bottom if this is ever not true (the main test will fail as a + // result, but we still need to unwind the recursive faults). + badhandler_on_sigaltstack = false; + } + if (current_ss < badhandler_low_water_mark) { + // Record the low point for the signal stack. We never expected this to be + // before stack bottom, but this is asserted in the actual test. + badhandler_low_water_mark = current_ss; + } + if (badhandler_recursive_faults > 0) { + badhandler_recursive_faults--; + Fault(); + } + FixupFault(reinterpret_cast(arg)); +} + +TEST(SigaltstackTest, WalksOffBottom) { + // This test marks the upper half of the stack_mem array as the signal stack. + // It asserts that when a fault occurs in the handler (already on the signal + // stack), we eventually continue to fault our way off the stack. We should + // not revert to the top of the signal stack when we fall off the bottom and + // the signal stack should remain "in use". When we fall off the signal stack, + // we should have an unconditional signal delivered and not start using the + // first part of the stack_mem array. + std::vector stack_mem(SIGSTKSZ * 2); + stack_t stack = {}; + stack.ss_sp = stack_mem.data() + SIGSTKSZ; // See above: upper half. + stack.ss_size = SIGSTKSZ; // Only one half the array. + auto const cleanup_sigstack = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); + + // Setup the handler: this must be for SIGSEGV, and it must allow proper + // nesting (no signal mask, no defer) so that we can trigger multiple times. + // + // When we walk off the bottom of the signal stack and force signal delivery + // of a SIGSEGV, the handler will revert to the default behavior (kill). + struct sigaction sa = {}; + sa.sa_sigaction = badhandler; + sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER; + auto const cleanup_sa = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); + + // Trigger a single fault. + badhandler_low_water_mark = + reinterpret_cast(&stack.ss_sp) + SIGSTKSZ; // Expected top. + badhandler_recursive_faults = 0; // Disable refault. + Fault(); + EXPECT_TRUE(badhandler_on_sigaltstack); + EXPECT_THAT(sigaltstack(nullptr, &stack), SyscallSucceeds()); + EXPECT_EQ(stack.ss_flags & SS_ONSTACK, 0); + EXPECT_LT(badhandler_low_water_mark, + reinterpret_cast(stack.ss_sp) + 2 * SIGSTKSZ); + EXPECT_GT(badhandler_low_water_mark, reinterpret_cast(stack.ss_sp)); + + // Trigger two faults. + char* prev_low_water_mark = badhandler_low_water_mark; // Previous top. + badhandler_recursive_faults = 1; // One refault. + Fault(); + ASSERT_TRUE(badhandler_on_sigaltstack); + EXPECT_THAT(sigaltstack(nullptr, &stack), SyscallSucceeds()); + EXPECT_EQ(stack.ss_flags & SS_ONSTACK, 0); + EXPECT_LT(badhandler_low_water_mark, prev_low_water_mark); + EXPECT_GT(badhandler_low_water_mark, reinterpret_cast(stack.ss_sp)); + + // Calculate the stack growth for a fault, and set the recursive faults to + // ensure that the signal handler stack required exceeds our marked stack area + // by a minimal amount. It should remain in the valid stack_mem area so that + // we can test the signal is forced merely by going out of the signal stack + // bounds, not by a genuine fault. + uintptr_t frame_size = + static_cast(prev_low_water_mark - badhandler_low_water_mark); + badhandler_recursive_faults = (SIGSTKSZ + frame_size) / frame_size; + EXPECT_EXIT(Fault(), ::testing::KilledBySignal(SIGSEGV), ""); +} + +volatile int setonstack_retval = 0; // Set by the handler. +volatile int setonstack_errno = 0; // Set by the handler. + +void setonstack(int sig, siginfo_t* siginfo, void* arg) { + char stack_mem[SIGSTKSZ]; + stack_t stack = {}; + stack.ss_sp = &stack_mem[0]; + stack.ss_size = SIGSTKSZ; + setonstack_retval = sigaltstack(&stack, nullptr); + setonstack_errno = errno; + FixupFault(reinterpret_cast(arg)); +} + +TEST(SigaltstackTest, SetWhileOnStack) { + // Reserve twice as much stack here, since the handler will allocate a vector + // of size SIGTKSZ and attempt to set the sigaltstack to that value. + std::vector stack_mem(2 * SIGSTKSZ); + stack_t stack = {}; + stack.ss_sp = stack_mem.data(); + stack.ss_size = stack_mem.size(); + auto const cleanup_sigstack = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); + + // See above. + struct sigaction sa = {}; + sa.sa_sigaction = setonstack; + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + auto const cleanup_sa = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); + + // Trigger a fault. + Fault(); + + // The set should have failed. + EXPECT_EQ(setonstack_retval, -1); + EXPECT_EQ(setonstack_errno, EPERM); +} + +TEST(SigaltstackTest, SetCurrentStack) { + // This is executed as an exit test because once the signal stack is set to + // the local stack, there's no good way to unwind. We don't want to taint the + // test of any other tests that might run within this process. + EXPECT_EXIT( + { + char stack_value = 0; + stack_t stack = {}; + stack.ss_sp = &stack_value - kPageSize; // Lower than current level. + stack.ss_size = 2 * kPageSize; // => &stack_value +/- kPageSize. + TEST_CHECK(sigaltstack(&stack, nullptr) == 0); + TEST_CHECK(sigaltstack(nullptr, &stack) == 0); + TEST_CHECK((stack.ss_flags & SS_ONSTACK) != 0); + + // Should not be able to change the stack (even no-op). + TEST_CHECK(sigaltstack(&stack, nullptr) == -1 && errno == EPERM); + + // Should not be able to disable the stack. + stack.ss_flags = SS_DISABLE; + TEST_CHECK(sigaltstack(&stack, nullptr) == -1 && errno == EPERM); + exit(0); + }, + ::testing::ExitedWithCode(0), ""); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sigaltstack_check.cc b/test/syscalls/linux/sigaltstack_check.cc new file mode 100644 index 000000000..b71f812a8 --- /dev/null +++ b/test/syscalls/linux/sigaltstack_check.cc @@ -0,0 +1,33 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Checks that there is no alternate signal stack by default. +// +// Used by a test in sigaltstack.cc. +#include +#include +#include +#include +#include + +#include "test/util/logging.h" + +int main(int /* argc */, char** /* argv */) { + stack_t stack; + TEST_CHECK(sigaltstack(nullptr, &stack) >= 0); + TEST_CHECK(stack.ss_flags == SS_DISABLE); + TEST_CHECK(stack.ss_sp == 0); + TEST_CHECK(stack.ss_size == 0); + return 0; +} diff --git a/test/syscalls/linux/sigiret.cc b/test/syscalls/linux/sigiret.cc new file mode 100644 index 000000000..1b7cecccb --- /dev/null +++ b/test/syscalls/linux/sigiret.cc @@ -0,0 +1,137 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr uint64_t kOrigRcx = 0xdeadbeeffacefeed; +constexpr uint64_t kOrigR11 = 0xfacefeedbaad1dea; + +volatile int gotvtalrm, ready; + +void sigvtalrm(int sig, siginfo_t* siginfo, void* _uc) { + ucontext_t* uc = reinterpret_cast(_uc); + + // Verify that: + // - test is in the busy-wait loop waiting for signal. + // - %rcx and %r11 values in mcontext_t match kOrigRcx and kOrigR11. + if (ready && + static_cast(uc->uc_mcontext.gregs[REG_RCX]) == kOrigRcx && + static_cast(uc->uc_mcontext.gregs[REG_R11]) == kOrigR11) { + // Modify the values %rcx and %r11 in the ucontext. These are the + // values seen by the application after the signal handler returns. + uc->uc_mcontext.gregs[REG_RCX] = ~kOrigRcx; + uc->uc_mcontext.gregs[REG_R11] = ~kOrigR11; + gotvtalrm = 1; + } +} + +TEST(SigIretTest, CheckRcxR11) { + // Setup signal handler for SIGVTALRM. + struct sigaction sa = {}; + sigfillset(&sa.sa_mask); + sa.sa_sigaction = sigvtalrm; + sa.sa_flags = SA_SIGINFO; + auto const action_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGVTALRM, sa)); + + auto const mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGVTALRM)); + + // Setup itimer to fire after 500 msecs. + struct itimerval itimer = {}; + itimer.it_value.tv_usec = 500 * 1000; // 500 msecs. + auto const timer_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_VIRTUAL, itimer)); + + // Initialize %rcx and %r11 and spin until the signal handler returns. + uint64_t rcx = kOrigRcx; + uint64_t r11 = kOrigR11; + asm volatile( + "movq %[rcx], %%rcx;" // %rcx = rcx + "movq %[r11], %%r11;" // %r11 = r11 + "movl $1, %[ready];" // ready = 1 + "1: pause; cmpl $0, %[gotvtalrm]; je 1b;" // while (!gotvtalrm); + "movq %%rcx, %[rcx];" // rcx = %rcx + "movq %%r11, %[r11];" // r11 = %r11 + : [ready] "=m"(ready), [rcx] "+m"(rcx), [r11] "+m"(r11) + : [gotvtalrm] "m"(gotvtalrm) + : "cc", "memory", "rcx", "r11"); + + // If sigreturn(2) returns via 'sysret' then %rcx and %r11 will be + // clobbered and set to 'ptregs->rip' and 'ptregs->rflags' respectively. + // + // The following check verifies that %rcx and %r11 were not clobbered + // when returning from the signal handler (via sigreturn(2)). + EXPECT_EQ(rcx, ~kOrigRcx); + EXPECT_EQ(r11, ~kOrigR11); +} + +constexpr uint64_t kNonCanonicalRip = 0xCCCC000000000000; + +// Test that a non-canonical signal handler faults as expected. +TEST(SigIretTest, BadHandler) { + struct sigaction sa = {}; + sa.sa_sigaction = + reinterpret_cast(kNonCanonicalRip); + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa)); + + pid_t pid = fork(); + if (pid == 0) { + // Child, wait for signal. + while (1) { + pause(); + } + } + ASSERT_THAT(pid, SyscallSucceeds()); + + EXPECT_THAT(kill(pid, SIGUSR1), SyscallSucceeds()); + + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) + << "status = " << status; +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + // SigIretTest.CheckRcxR11 depends on delivering SIGVTALRM to the main thread. + // Block SIGVTALRM so that any other threads created by TestInit will also + // have SIGVTALRM blocked. + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGVTALRM); + TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); + + gvisor::testing::TestInit(&argc, &argv); + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/sigprocmask.cc b/test/syscalls/linux/sigprocmask.cc new file mode 100644 index 000000000..d8b918446 --- /dev/null +++ b/test/syscalls/linux/sigprocmask.cc @@ -0,0 +1,272 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Signals numbers used for testing. +static constexpr int kTestSignal1 = SIGUSR1; +static constexpr int kTestSignal2 = SIGUSR2; + +static int raw_sigprocmask(int how, const sigset_t* set, sigset_t* oldset) { + return syscall(SYS_rt_sigprocmask, how, set, oldset, _NSIG / 8); +} + +// count of the number of signals received +int signal_count[kMaxSignal + 1]; + +// signal handler increments the signal counter +void SigHandler(int sig, siginfo_t* info, void* context) { + TEST_CHECK(sig > 0 && sig <= kMaxSignal); + signal_count[sig] += 1; +} + +// The test fixture saves and restores the signal mask and +// sets up handlers for kTestSignal1 and kTestSignal2. +class SigProcMaskTest : public ::testing::Test { + protected: + void SetUp() override { + // Save the current signal mask. + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &mask_), + SyscallSucceeds()); + + // Setup signal handlers for kTestSignal1 and kTestSignal2. + struct sigaction sa; + sa.sa_sigaction = SigHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + EXPECT_THAT(sigaction(kTestSignal1, &sa, &sa_test_sig_1_), + SyscallSucceeds()); + EXPECT_THAT(sigaction(kTestSignal2, &sa, &sa_test_sig_2_), + SyscallSucceeds()); + + // Clear the signal counters. + memset(signal_count, 0, sizeof(signal_count)); + } + + void TearDown() override { + // Restore the signal mask. + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &mask_, nullptr), + SyscallSucceeds()); + + // Restore the signal handlers for kTestSignal1 and kTestSignal2. + EXPECT_THAT(sigaction(kTestSignal1, &sa_test_sig_1_, nullptr), + SyscallSucceeds()); + EXPECT_THAT(sigaction(kTestSignal2, &sa_test_sig_2_, nullptr), + SyscallSucceeds()); + } + + private: + sigset_t mask_; + struct sigaction sa_test_sig_1_; + struct sigaction sa_test_sig_2_; +}; + +// Both sigsets nullptr should succeed and do nothing. +TEST_F(SigProcMaskTest, NullAddress) { + EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, nullptr, NULL), SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, nullptr, NULL), SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, NULL), SyscallSucceeds()); +} + +// Bad address for either sigset should fail with EFAULT. +TEST_F(SigProcMaskTest, BadAddress) { + sigset_t* bad_addr = reinterpret_cast(-1); + + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, bad_addr, nullptr), + SyscallFailsWithErrno(EFAULT)); + + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, bad_addr), + SyscallFailsWithErrno(EFAULT)); +} + +// Bad value of the "how" parameter should fail with EINVAL. +TEST_F(SigProcMaskTest, BadParameter) { + int bad_param_1 = -1; + int bad_param_2 = 42; + + sigset_t set1; + sigemptyset(&set1); + + EXPECT_THAT(raw_sigprocmask(bad_param_1, &set1, nullptr), + SyscallFailsWithErrno(EINVAL)); + + EXPECT_THAT(raw_sigprocmask(bad_param_2, &set1, nullptr), + SyscallFailsWithErrno(EINVAL)); +} + +// Check that we can get the current signal mask. +TEST_F(SigProcMaskTest, GetMask) { + sigset_t set1; + sigset_t set2; + + sigemptyset(&set1); + sigfillset(&set2); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &set1), SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &set2), SyscallSucceeds()); + EXPECT_THAT(set1, EqualsSigset(set2)); +} + +// Check that we can set the signal mask. +TEST_F(SigProcMaskTest, SetMask) { + sigset_t actual; + sigset_t expected; + + // Try to mask all signals + sigfillset(&expected); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), + SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), + SyscallSucceeds()); + // sigprocmask() should have silently ignored SIGKILL and SIGSTOP. + sigdelset(&expected, SIGSTOP); + sigdelset(&expected, SIGKILL); + EXPECT_THAT(actual, EqualsSigset(expected)); + + // Try to clear the signal mask + sigemptyset(&expected); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), + SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), + SyscallSucceeds()); + EXPECT_THAT(actual, EqualsSigset(expected)); + + // Try to set a mask with one signal. + sigemptyset(&expected); + sigaddset(&expected, kTestSignal1); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), + SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), + SyscallSucceeds()); + EXPECT_THAT(actual, EqualsSigset(expected)); +} + +// Check that we can add and remove signals. +TEST_F(SigProcMaskTest, BlockUnblock) { + sigset_t actual; + sigset_t expected; + + // Try to set a mask with one signal. + sigemptyset(&expected); + sigaddset(&expected, kTestSignal1); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), + SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), + SyscallSucceeds()); + EXPECT_THAT(actual, EqualsSigset(expected)); + + // Try to add another signal. + sigset_t block; + sigemptyset(&block); + sigaddset(&block, kTestSignal2); + EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, &block, nullptr), SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), + SyscallSucceeds()); + sigaddset(&expected, kTestSignal2); + EXPECT_THAT(actual, EqualsSigset(expected)); + + // Try to remove a signal. + sigset_t unblock; + sigemptyset(&unblock); + sigaddset(&unblock, kTestSignal1); + EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, &unblock, nullptr), + SyscallSucceeds()); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), + SyscallSucceeds()); + sigdelset(&expected, kTestSignal1); + EXPECT_THAT(actual, EqualsSigset(expected)); +} + +// Test that the signal mask actually blocks signals. +TEST_F(SigProcMaskTest, SignalHandler) { + sigset_t mask; + + // clear the signal mask + sigemptyset(&mask); + EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, nullptr), SyscallSucceeds()); + + // Check the initial signal counts. + EXPECT_EQ(0, signal_count[kTestSignal1]); + EXPECT_EQ(0, signal_count[kTestSignal2]); + + // Check that both kTestSignal1 and kTestSignal2 are not blocked. + raise(kTestSignal1); + raise(kTestSignal2); + EXPECT_EQ(1, signal_count[kTestSignal1]); + EXPECT_EQ(1, signal_count[kTestSignal2]); + + // Block kTestSignal1. + sigaddset(&mask, kTestSignal1); + EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, &mask, nullptr), SyscallSucceeds()); + + // Check that kTestSignal1 is blocked. + raise(kTestSignal1); + raise(kTestSignal2); + EXPECT_EQ(1, signal_count[kTestSignal1]); + EXPECT_EQ(2, signal_count[kTestSignal2]); + + // Unblock kTestSignal1. + sigaddset(&mask, kTestSignal1); + EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, &mask, nullptr), SyscallSucceeds()); + + // Check that the unblocked kTestSignal1 has been delivered. + // TODO: gvisor currently drops masked signals on the floor. + if (!IsRunningOnGvisor()) { + EXPECT_EQ(2, signal_count[kTestSignal1]); + } + EXPECT_EQ(2, signal_count[kTestSignal2]); +} + +// Check that sigprocmask correctly handles aliasing of the set and oldset +// pointers. +TEST_F(SigProcMaskTest, AliasedSets) { + sigset_t mask; + + // Set a mask in which only kTestSignal1 is blocked. + sigset_t mask1; + sigemptyset(&mask1); + sigaddset(&mask1, kTestSignal1); + mask = mask1; + ASSERT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, nullptr), SyscallSucceeds()); + + // Exchange it with a mask in which only kTestSignal2 is blocked. + sigset_t mask2; + sigemptyset(&mask2); + sigaddset(&mask2, kTestSignal2); + mask = mask2; + ASSERT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, &mask), SyscallSucceeds()); + + // Check that the exchange succeeeded: + // mask should now contain the previously-set mask blocking only kTestSignal1. + EXPECT_THAT(mask, EqualsSigset(mask1)); + // The current mask should block only kTestSignal2. + ASSERT_THAT(raw_sigprocmask(0, nullptr, &mask), SyscallSucceeds()); + EXPECT_THAT(mask, EqualsSigset(mask2)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sigstop.cc b/test/syscalls/linux/sigstop.cc new file mode 100644 index 000000000..e21d23d51 --- /dev/null +++ b/test/syscalls/linux/sigstop.cc @@ -0,0 +1,150 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_bool(sigstop_test_child, false, + "If true, run the SigstopTest child workload."); + +namespace gvisor { +namespace testing { + +namespace { + +constexpr absl::Duration kChildStartupDelay = absl::Seconds(5); +constexpr absl::Duration kChildMainThreadDelay = absl::Seconds(10); +constexpr absl::Duration kChildExtraThreadDelay = absl::Seconds(15); +constexpr absl::Duration kPostSIGSTOPDelay = absl::Seconds(20); + +// Comparisons on absl::Duration aren't yet constexpr (2017-07-14), so we +// can't just use static_assert. +TEST(SigstopTest, TimesAreRelativelyConsistent) { + EXPECT_LT(kChildStartupDelay, kChildMainThreadDelay) + << "Child process will exit before the parent process attempts to stop " + "it"; + EXPECT_LT(kChildMainThreadDelay, kChildExtraThreadDelay) + << "Secondary thread in child process will exit before main thread, " + "causing it to exit with the wrong code"; + EXPECT_LT(kChildExtraThreadDelay, kPostSIGSTOPDelay) + << "Parent process stops waiting before child process may exit if " + "improperly stopped, rendering the test ineffective"; +} + +// Exit codes communicated from the child workload to the parent test process. +constexpr int kChildMainThreadExitCode = 10; +constexpr int kChildExtraThreadExitCode = 11; + +TEST(SigstopTest, Correctness) { + pid_t child_pid = -1; + int execve_errno = 0; + auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec("/proc/self/exe", {"/proc/self/exe", "--sigstop_test_child"}, + {}, nullptr, &child_pid, &execve_errno)); + + ASSERT_GT(child_pid, 0); + ASSERT_EQ(execve_errno, 0); + + // Wait for the child subprocess to start the second thread before stopping + // it. + absl::SleepFor(kChildStartupDelay); + ASSERT_THAT(kill(child_pid, SIGSTOP), SyscallSucceeds()); + int status; + EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, WUNTRACED), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFSTOPPED(status)); + EXPECT_EQ(SIGSTOP, WSTOPSIG(status)); + + // Sleep for longer than either of the sleeps in the child subprocess, + // expecting the child to stay alive because it's stopped. + absl::SleepFor(kPostSIGSTOPDelay); + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, WNOHANG), + SyscallSucceedsWithValue(0)); + + // Resume the child. + ASSERT_THAT(kill(child_pid, SIGCONT), SyscallSucceeds()); + + EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, WCONTINUED), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFCONTINUED(status)); + + // Expect it to die. + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), kChildMainThreadExitCode); +} + +// Like base:SleepFor, but tries to avoid counting time spent stopped due to a +// stop signal toward the sleep. +// +// This is required due to an inconsistency in how nanosleep(2) and stop signals +// interact on Linux. When nanosleep is interrupted, it writes the remaining +// time back to its second timespec argument, so that if nanosleep is +// interrupted by a signal handler then userspace can immediately call nanosleep +// again with that timespec. However, if nanosleep is automatically restarted +// (because it's interrupted by a signal that is not delivered to a handler, +// such as a stop signal), it's restarted based on the timer's former *absolute* +// expiration time (via ERESTART_RESTARTBLOCK => SYS_restart_syscall => +// hrtimer_nanosleep_restart). This means that time spent stopped is effectively +// counted as time spent sleeping, resulting in less time spent sleeping than +// expected. +// +// Dividing the sleep into multiple smaller sleeps limits the impact of this +// effect to the length of each sleep during which a stop occurs; for example, +// if a sleeping process is only stopped once, SleepIgnoreStopped can +// under-sleep by at most 100ms. +void SleepIgnoreStopped(absl::Duration d) { + absl::Duration const max_sleep = absl::Milliseconds(100); + while (d > absl::ZeroDuration()) { + absl::Duration to_sleep = std::min(d, max_sleep); + absl::SleepFor(to_sleep); + d -= to_sleep; + } +} + +void RunChild() { + // Start another thread that attempts to call exit_group with a different + // error code, in order to verify that SIGSTOP stops this thread as well. + ScopedThread t([] { + SleepIgnoreStopped(kChildExtraThreadDelay); + exit(kChildExtraThreadExitCode); + }); + SleepIgnoreStopped(kChildMainThreadDelay); + exit(kChildMainThreadExitCode); +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + gvisor::testing::TestInit(&argc, &argv); + + if (FLAGS_sigstop_test_child) { + gvisor::testing::RunChild(); + return 1; + } + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/sigtimedwait.cc b/test/syscalls/linux/sigtimedwait.cc new file mode 100644 index 000000000..3a350fc28 --- /dev/null +++ b/test/syscalls/linux/sigtimedwait.cc @@ -0,0 +1,248 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// N.B. main() blocks SIGALRM and SIGCHLD on all threads. + +constexpr int kAlarmSecs = 12; + +void NoopHandler(int sig, siginfo_t* info, void* context) {} + +TEST(SigtimedwaitTest, InvalidTimeout) { + sigset_t mask; + sigemptyset(&mask); + struct timespec timeout = {0, 1000000001}; + EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), + SyscallFailsWithErrno(EINVAL)); + timeout = {-1, 0}; + EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), + SyscallFailsWithErrno(EINVAL)); + timeout = {0, -1}; + EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), + SyscallFailsWithErrno(EINVAL)); +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and wait. +TEST(SigtimedwaitTest, AlarmReturnsAlarm_NoRandomSave) { + struct itimerval itv = {}; + itv.it_value.tv_sec = kAlarmSecs; + const auto itimer_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, itv)); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGALRM); + siginfo_t info = {}; + EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, &info, nullptr), + SyscallSucceedsWithValue(SIGALRM)); + EXPECT_EQ(SIGALRM, info.si_signo); +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and wait. +TEST(SigtimedwaitTest, NullTimeoutReturnsEINTR_NoRandomSave) { + struct sigaction sa; + sa.sa_sigaction = NoopHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + const auto action_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + const auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + struct itimerval itv = {}; + itv.it_value.tv_sec = kAlarmSecs; + const auto itimer_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, itv)); + + sigset_t mask; + sigemptyset(&mask); + EXPECT_THAT(sigtimedwait(&mask, nullptr, nullptr), + SyscallFailsWithErrno(EINTR)); +} + +TEST(SigtimedwaitTest, LegitTimeoutReturnsEAGAIN) { + sigset_t mask; + sigemptyset(&mask); + struct timespec timeout = {1, 0}; // 1 second + EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &timeout), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(SigtimedwaitTest, ZeroTimeoutReturnsEAGAIN) { + sigset_t mask; + sigemptyset(&mask); + struct timespec timeout = {0, 0}; // 0 second + EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(SigtimedwaitTest, KillGeneratedSIGCHLD) { + EXPECT_THAT(kill(getpid(), SIGCHLD), SyscallSucceeds()); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + struct timespec ts = {5, 0}; + EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &ts), + SyscallSucceedsWithValue(SIGCHLD)); +} + +TEST(SigtimedwaitTest, ChildExitGeneratedSIGCHLD) { + pid_t pid = fork(); + if (pid == 0) { + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status; + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + struct timespec ts = {5, 0}; + EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &ts), + SyscallSucceedsWithValue(SIGCHLD)); +} + +TEST(SigtimedwaitTest, ChildExitGeneratedSIGCHLDWithHandler) { + // Setup handler for SIGCHLD, but don't unblock it. + struct sigaction sa; + sa.sa_sigaction = NoopHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + const auto action_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa)); + + pid_t pid = fork(); + if (pid == 0) { + _exit(0); + } + ASSERT_THAT(pid, SyscallSucceeds()); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + struct timespec ts = {5, 0}; + EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &ts), + SyscallSucceedsWithValue(SIGCHLD)); + + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status; +} + +TEST(SigtimedwaitTest, IgnoredUnmaskedSignal) { + constexpr int kSigno = SIGUSR1; + constexpr auto kSigtimedwaitSetupTime = absl::Seconds(2); + constexpr auto kSigtimedwaitTimeout = absl::Seconds(5); + ASSERT_GT(kSigtimedwaitTimeout, kSigtimedwaitSetupTime); + + // Ensure that kSigno is ignored, and unmasked on this thread. + struct sigaction sa = {}; + sa.sa_handler = SIG_IGN; + const auto scoped_sigaction = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, kSigno); + auto scoped_sigmask = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, mask)); + + // Create a thread which will send us kSigno while we are blocked in + // sigtimedwait. + pid_t tid = gettid(); + ScopedThread sigthread([&] { + absl::SleepFor(kSigtimedwaitSetupTime); + EXPECT_THAT(tgkill(getpid(), tid, kSigno), SyscallSucceeds()); + }); + + // sigtimedwait should not observe kSigno since it is ignored and already + // unmasked, causing it to be dropped before it is enqueued. + struct timespec timeout_ts = absl::ToTimespec(kSigtimedwaitTimeout); + EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &timeout_ts), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(SigtimedwaitTest, IgnoredMaskedSignal) { + constexpr int kSigno = SIGUSR1; + constexpr auto kSigtimedwaitSetupTime = absl::Seconds(2); + constexpr auto kSigtimedwaitTimeout = absl::Seconds(5); + ASSERT_GT(kSigtimedwaitTimeout, kSigtimedwaitSetupTime); + + // Ensure that kSigno is ignored, and masked on this thread. + struct sigaction sa = {}; + sa.sa_handler = SIG_IGN; + const auto scoped_sigaction = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, kSigno); + auto scoped_sigmask = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); + + // Create a thread which will send us kSigno while we are blocked in + // sigtimedwait. + pid_t tid = gettid(); + ScopedThread sigthread([&] { + absl::SleepFor(kSigtimedwaitSetupTime); + EXPECT_THAT(tgkill(getpid(), tid, kSigno), SyscallSucceeds()); + }); + + // sigtimedwait should observe kSigno since it is normally masked, causing it + // to be enqueued despite being ignored. + struct timespec timeout_ts = absl::ToTimespec(kSigtimedwaitTimeout); + EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &timeout_ts), + SyscallSucceedsWithValue(kSigno)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + // These tests depend on delivering SIGALRM/SIGCHLD to the main thread or in + // sigtimedwait. Block them so that any other threads created by TestInit will + // also have them blocked. + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGCHLD); + TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); + + gvisor::testing::TestInit(&argc, &argv); + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/socket_abstract.cc b/test/syscalls/linux/socket_abstract.cc new file mode 100644 index 000000000..7b111a2dd --- /dev/null +++ b/test/syscalls/linux/socket_abstract.cc @@ -0,0 +1,43 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_generic.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_unix.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_filesystem.cc b/test/syscalls/linux/socket_filesystem.cc new file mode 100644 index 000000000..eea6f2810 --- /dev/null +++ b/test/syscalls/linux/socket_filesystem.cc @@ -0,0 +1,43 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_generic.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_unix.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_generic.cc b/test/syscalls/linux/socket_generic.cc new file mode 100644 index 000000000..fbc3bebed --- /dev/null +++ b/test/syscalls/linux/socket_generic.cc @@ -0,0 +1,403 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_generic.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +// This file is a generic socket test file. It must be built with another file +// that provides the test types. + +namespace gvisor { +namespace testing { + +TEST_P(AllSocketPairTest, BasicReadWrite) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char buf[20]; + const std::string data = "abc"; + ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), 3), + SyscallSucceedsWithValue(3)); + ASSERT_THAT(ReadFd(sockets->second_fd(), buf, 3), + SyscallSucceedsWithValue(3)); + EXPECT_EQ(data, absl::string_view(buf, 3)); +} + +TEST_P(AllSocketPairTest, BasicSendRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(AllSocketPairTest, BasicSendmmsg) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[200]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + std::vector msgs(10); + std::vector iovs(msgs.size()); + const int chunk_size = sizeof(sent_data) / msgs.size(); + for (size_t i = 0; i < msgs.size(); i++) { + iovs[i].iov_len = chunk_size; + iovs[i].iov_base = &sent_data[i * chunk_size]; + msgs[i].msg_hdr.msg_iov = &iovs[i]; + msgs[i].msg_hdr.msg_iovlen = 1; + } + + ASSERT_THAT( + RetryEINTR(sendmmsg)(sockets->first_fd(), &msgs[0], msgs.size(), 0), + SyscallSucceedsWithValue(msgs.size())); + + for (const struct mmsghdr& msg : msgs) { + EXPECT_EQ(chunk_size, msg.msg_len); + } + + char received_data[sizeof(sent_data)]; + for (size_t i = 0; i < msgs.size(); i++) { + ASSERT_THAT(ReadFd(sockets->second_fd(), &received_data[i * chunk_size], + chunk_size), + SyscallSucceedsWithValue(chunk_size)); + } + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(AllSocketPairTest, BasicRecvmmsg) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[200]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + char received_data[sizeof(sent_data)]; + std::vector msgs(10); + std::vector iovs(msgs.size()); + const int chunk_size = sizeof(sent_data) / msgs.size(); + for (size_t i = 0; i < msgs.size(); i++) { + iovs[i].iov_len = chunk_size; + iovs[i].iov_base = &received_data[i * chunk_size]; + msgs[i].msg_hdr.msg_iov = &iovs[i]; + msgs[i].msg_hdr.msg_iovlen = 1; + } + + for (size_t i = 0; i < msgs.size(); i++) { + ASSERT_THAT( + WriteFd(sockets->first_fd(), &sent_data[i * chunk_size], chunk_size), + SyscallSucceedsWithValue(chunk_size)); + } + + ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->second_fd(), &msgs[0], msgs.size(), + 0, nullptr), + SyscallSucceedsWithValue(msgs.size())); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + for (const struct mmsghdr& msg : msgs) { + EXPECT_EQ(chunk_size, msg.msg_len); + } +} + +TEST_P(AllSocketPairTest, SendmsgRecvmsg10KB) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + std::vector sent_data(10 * 1024); + RandomizeBuffer(sent_data.data(), sent_data.size()); + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size())); + + std::vector received_data(sent_data.size()); + ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->second_fd(), received_data.data(), + received_data.size())); + + EXPECT_EQ(0, + memcmp(sent_data.data(), received_data.data(), sent_data.size())); +} + +// This test validates that a sendmsg/recvmsg w/ MSG_CTRUNC is a no-op on +// input flags. +TEST_P(AllSocketPairTest, SendmsgRecvmsgMsgCtruncNoop) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + std::vector sent_data(10 * 1024); + RandomizeBuffer(sent_data.data(), sent_data.size()); + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size())); + + std::vector received_data(sent_data.size()); + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct iovec iov; + iov.iov_base = &received_data[0]; + iov.iov_len = received_data.size(); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // MSG_CTRUNC should be a no-op. + ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_CTRUNC), + SyscallSucceedsWithValue(received_data.size())); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + EXPECT_EQ(cmsg, nullptr); + EXPECT_EQ(msg.msg_controllen, 0); + EXPECT_EQ(0, + memcmp(sent_data.data(), received_data.data(), sent_data.size())); +} + +TEST_P(AllSocketPairTest, SendmsgRecvmsg16KB) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + std::vector sent_data(16 * 1024); + RandomizeBuffer(sent_data.data(), sent_data.size()); + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size())); + + std::vector received_data(sent_data.size()); + ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->second_fd(), received_data.data(), + received_data.size())); + + EXPECT_EQ(0, + memcmp(sent_data.data(), received_data.data(), sent_data.size())); +} + +TEST_P(AllSocketPairTest, RecvmmsgInvalidTimeout) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char buf[10]; + struct mmsghdr msg = {}; + struct iovec iov = {}; + iov.iov_len = sizeof(buf); + iov.iov_base = buf; + msg.msg_hdr.msg_iov = &iov; + msg.msg_hdr.msg_iovlen = 1; + struct timespec timeout = {-1, -1}; + ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->first_fd(), &msg, 1, 0, &timeout), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(AllSocketPairTest, RecvmmsgTimeoutBeforeRecv) { + // There is a known bug in the Linux recvmmsg(2) causing it to block forever + // if the timeout expires while blocking for the first message. + SKIP_IF(!IsRunningOnGvisor()); + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char buf[10]; + struct mmsghdr msg = {}; + struct iovec iov = {}; + iov.iov_len = sizeof(buf); + iov.iov_base = buf; + msg.msg_hdr.msg_iov = &iov; + msg.msg_hdr.msg_iovlen = 1; + struct timespec timeout = {}; + ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->first_fd(), &msg, 1, 0, &timeout), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(AllSocketPairTest, MsgPeek) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[50]; + memset(&sent_data, 0, sizeof(sent_data)); + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[sizeof(sent_data)]; + for (int i = 0; i < 3; i++) { + memset(received_data, 0, sizeof(received_data)); + EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_PEEK), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); + } + + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); +} + +TEST_P(AllSocketPairTest, LingerSocketOption) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + struct linger got_linger = {-1, -1}; + socklen_t length = sizeof(struct linger); + EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, + &got_linger, &length), + SyscallSucceedsWithValue(0)); + struct linger want_linger = {}; + EXPECT_EQ(0, memcmp(&want_linger, &got_linger, sizeof(struct linger))); + EXPECT_EQ(sizeof(struct linger), length); +} + +TEST_P(AllSocketPairTest, KeepAliveSocketOption) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + int keepalive = -1; + socklen_t length = sizeof(int); + EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, + &keepalive, &length), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(0, keepalive); + EXPECT_EQ(sizeof(int), length); +} + +TEST_P(AllSocketPairTest, RcvBufSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + int size = 0; + socklen_t size_size = sizeof(size); + EXPECT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &size, &size_size), + SyscallSucceeds()); + EXPECT_GT(size, 0); +} + +TEST_P(AllSocketPairTest, SndBufSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + int size = 0; + socklen_t size_size = sizeof(size); + EXPECT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &size, &size_size), + SyscallSucceeds()); + EXPECT_GT(size, 0); +} + +TEST_P(AllSocketPairTest, RecvTimeoutSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct timeval tv { + .tv_sec = 0, .tv_usec = 10 + }; + EXPECT_THAT( + setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), + SyscallSucceeds()); + + char buf[20] = {}; + EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(AllSocketPairTest, RecvTimeoutOneSecondSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct timeval tv { + .tv_sec = 1, .tv_usec = 0 + }; + EXPECT_THAT( + setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), + SyscallSucceeds()); + + char buf[20] = {}; + EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(AllSocketPairTest, RecvmsgTimeoutSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct timeval tv { + .tv_sec = 0, .tv_usec = 10 + }; + EXPECT_THAT( + setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), + SyscallSucceeds()); + + struct msghdr msg = {}; + char buf[20] = {}; + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(AllSocketPairTest, SoRcvTimeoIsSet) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct timeval tv { + .tv_sec = 0, .tv_usec = 35 + }; + EXPECT_THAT( + setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, SoRcvTimeoIsSetLargerArg) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct timeval_with_extra { + struct timeval tv; + int64_t extra_data; + } ABSL_ATTRIBUTE_PACKED; + + timeval_with_extra tv_extra; + tv_extra.tv.tv_sec = 0; + tv_extra.tv.tv_usec = 25; + + EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, + &tv_extra, sizeof(tv_extra)), + SyscallSucceeds()); +} + +TEST_P(AllSocketPairTest, RecvmsgTimeoutOneSecondSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct timeval tv { + .tv_sec = 1, .tv_usec = 0 + }; + EXPECT_THAT( + setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), + SyscallSucceeds()); + + struct msghdr msg = {}; + char buf[20] = {}; + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(AllSocketPairTest, RecvWaitAll) { + SKIP_IF(IsRunningOnGvisor()); // FIXME: Support MSG_WAITALL. + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[100]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[sizeof(sent_data)] = {}; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_WAITALL), + SyscallSucceedsWithValue(sizeof(sent_data))); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_generic.h b/test/syscalls/linux/socket_generic.h new file mode 100644 index 000000000..cd826abcf --- /dev/null +++ b/test/syscalls/linux/socket_generic.h @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of blocking and non-blocking +// connected stream sockets. +using AllSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_ diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc new file mode 100644 index 000000000..7bdbd7797 --- /dev/null +++ b/test/syscalls/linux/socket_inet_loopback.cc @@ -0,0 +1,812 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +PosixErrorOr AddrPort(int family, sockaddr_storage const& addr) { + switch (family) { + case AF_INET: + return static_cast( + reinterpret_cast(&addr)->sin_port); + case AF_INET6: + return static_cast( + reinterpret_cast(&addr)->sin6_port); + default: + return PosixError(EINVAL, + absl::StrCat("unknown socket family: ", family)); + } +} + +PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) { + switch (family) { + case AF_INET: + reinterpret_cast(addr)->sin_port = port; + return NoError(); + case AF_INET6: + reinterpret_cast(addr)->sin6_port = port; + return NoError(); + default: + return PosixError(EINVAL, + absl::StrCat("unknown socket family: ", family)); + } +} + +struct TestAddress { + std::string description; + sockaddr_storage addr; + socklen_t addr_len; + + int family() const { return addr.ss_family; } + explicit TestAddress(std::string description = "") + : description(std::move(description)), addr(), addr_len() {} +}; + +TestAddress V4Any() { + TestAddress t("V4Any"); + t.addr.ss_family = AF_INET; + t.addr_len = sizeof(sockaddr_in); + reinterpret_cast(&t.addr)->sin_addr.s_addr = htonl(INADDR_ANY); + return t; +} + +TestAddress V4Loopback() { + TestAddress t("V4Loopback"); + t.addr.ss_family = AF_INET; + t.addr_len = sizeof(sockaddr_in); + reinterpret_cast(&t.addr)->sin_addr.s_addr = + htonl(INADDR_LOOPBACK); + return t; +} + +TestAddress V4MappedAny() { + TestAddress t("V4MappedAny"); + t.addr.ss_family = AF_INET6; + t.addr_len = sizeof(sockaddr_in6); + inet_pton(AF_INET6, "::ffff:0.0.0.0", + reinterpret_cast(&t.addr)->sin6_addr.s6_addr); + return t; +} + +TestAddress V4MappedLoopback() { + TestAddress t("V4MappedLoopback"); + t.addr.ss_family = AF_INET6; + t.addr_len = sizeof(sockaddr_in6); + inet_pton(AF_INET6, "::ffff:127.0.0.1", + reinterpret_cast(&t.addr)->sin6_addr.s6_addr); + return t; +} + +TestAddress V6Any() { + TestAddress t("V6Any"); + t.addr.ss_family = AF_INET6; + t.addr_len = sizeof(sockaddr_in6); + reinterpret_cast(&t.addr)->sin6_addr = in6addr_any; + return t; +} + +TestAddress V6Loopback() { + TestAddress t("V6Loopback"); + t.addr.ss_family = AF_INET6; + t.addr_len = sizeof(sockaddr_in6); + reinterpret_cast(&t.addr)->sin6_addr = in6addr_loopback; + return t; +} + +struct TestParam { + TestAddress listener; + TestAddress connector; +}; + +std::string DescribeTestParam(::testing::TestParamInfo const& info) { + return absl::StrCat("Listen", info.param.listener.description, "_Connect", + info.param.connector.description); +} + +using SocketInetLoopbackTest = ::testing::TestWithParam; + +TEST(BadSocketPairArgs, ValidateErrForBadCallsToSocketPair) { + int fd[2] = {}; + + // Valid AF but invalid for socketpair(2) return ESOCKTNOSUPPORT. + ASSERT_THAT(socketpair(AF_INET, 0, 0, fd), + SyscallFailsWithErrno(ESOCKTNOSUPPORT)); + ASSERT_THAT(socketpair(AF_INET6, 0, 0, fd), + SyscallFailsWithErrno(ESOCKTNOSUPPORT)); + + // Invalid AF will return ENOAFSUPPORT. + ASSERT_THAT(socketpair(AF_MAX, 0, 0, fd), + SyscallFailsWithErrno(EAFNOSUPPORT)); + ASSERT_THAT(socketpair(8675309, 0, 0, fd), + SyscallFailsWithErrno(EAFNOSUPPORT)); +} + +TEST_P(SocketInetLoopbackTest, TCP) { + auto const& param = GetParam(); + + TestAddress const& listener = param.listener; + TestAddress const& connector = param.connector; + + // Create the listening socket. + const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE( + Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP)); + sockaddr_storage listen_addr = listener.addr; + ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast(&listen_addr), + listener.addr_len), + SyscallSucceeds()); + ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds()); + + // Get the port bound by the listening socket. + socklen_t addrlen = listener.addr_len; + ASSERT_THAT(getsockname(listen_fd.get(), + reinterpret_cast(&listen_addr), &addrlen), + SyscallSucceeds()); + uint16_t const port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); + + // Connect to the listening socket. + const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( + Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); + sockaddr_storage conn_addr = connector.addr; + ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); + ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), + reinterpret_cast(&conn_addr), + connector.addr_len), + SyscallSucceeds()); + + // Accept the connection. + ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); + + ASSERT_THAT(shutdown(listen_fd.get(), SHUT_RDWR), SyscallSucceeds()); + + ASSERT_THAT(shutdown(conn_fd.get(), SHUT_RDWR), SyscallSucceeds()); +} + +INSTANTIATE_TEST_CASE_P( + All, SocketInetLoopbackTest, + ::testing::Values( + // Listeners bound to IPv4 addresses refuse connections using IPv6 + // addresses. + TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()}, + TestParam{V4Any(), V4MappedAny()}, + TestParam{V4Any(), V4MappedLoopback()}, + TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()}, + TestParam{V4Loopback(), V4MappedLoopback()}, + TestParam{V4MappedAny(), V4Any()}, + TestParam{V4MappedAny(), V4Loopback()}, + TestParam{V4MappedAny(), V4MappedAny()}, + TestParam{V4MappedAny(), V4MappedLoopback()}, + TestParam{V4MappedLoopback(), V4Any()}, + TestParam{V4MappedLoopback(), V4Loopback()}, + TestParam{V4MappedLoopback(), V4MappedLoopback()}, + + // Listeners bound to IN6ADDR_ANY accept all connections. + TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()}, + TestParam{V6Any(), V4MappedAny()}, + TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()}, + TestParam{V6Any(), V6Loopback()}, + + // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4 + // addresses. + TestParam{V6Loopback(), V6Any()}, + TestParam{V6Loopback(), V6Loopback()}), + DescribeTestParam); + +struct ProtocolTestParam { + std::string description; + int type; +}; + +std::string DescribeProtocolTestParam( + ::testing::TestParamInfo const& info) { + return info.param.description; +} + +using SocketMultiProtocolInetLoopbackTest = + ::testing::TestWithParam; + +TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedLoopbackOnlyReservesV4) { + auto const& param = GetParam(); + + for (int i = 0; true; i++) { + // Bind the v4 loopback on a dual stack socket. + TestAddress const& test_addr_dual = V4MappedLoopback(); + sockaddr_storage addr_dual = test_addr_dual.addr; + const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_dual.family(), param.type, 0)); + ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast(&addr_dual), + test_addr_dual.addr_len), + SyscallSucceeds()); + + // Get the port that we bound. + socklen_t addrlen = test_addr_dual.addr_len; + ASSERT_THAT(getsockname(fd_dual.get(), + reinterpret_cast(&addr_dual), &addrlen), + SyscallSucceeds()); + uint16_t const port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); + + // Verify that we can still bind the v6 loopback on the same port. + TestAddress const& test_addr_v6 = V6Loopback(); + sockaddr_storage addr_v6 = test_addr_v6.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); + const FileDescriptor fd_v6 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); + int ret = bind(fd_v6.get(), reinterpret_cast(&addr_v6), + test_addr_v6.addr_len); + if (ret == -1 && errno == EADDRINUSE) { + // Port may have been in use. + ASSERT_LT(i, 100); // Give up after 100 tries. + continue; + } + ASSERT_THAT(ret, SyscallSucceeds()); + + // Verify that binding the v4 loopback with the same port on a v4 socket + // fails. + TestAddress const& test_addr_v4 = V4Loopback(); + sockaddr_storage addr_v4 = test_addr_v4.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port)); + const FileDescriptor fd_v4 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast(&addr_v4), + test_addr_v4.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // No need to try again. + break; + } +} + +TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedAnyOnlyReservesV4) { + auto const& param = GetParam(); + + for (int i = 0; true; i++) { + // Bind the v4 any on a dual stack socket. + TestAddress const& test_addr_dual = V4MappedAny(); + sockaddr_storage addr_dual = test_addr_dual.addr; + const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_dual.family(), param.type, 0)); + ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast(&addr_dual), + test_addr_dual.addr_len), + SyscallSucceeds()); + + // Get the port that we bound. + socklen_t addrlen = test_addr_dual.addr_len; + ASSERT_THAT(getsockname(fd_dual.get(), + reinterpret_cast(&addr_dual), &addrlen), + SyscallSucceeds()); + uint16_t const port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); + + // Verify that we can still bind the v6 loopback on the same port. + TestAddress const& test_addr_v6 = V6Loopback(); + sockaddr_storage addr_v6 = test_addr_v6.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); + const FileDescriptor fd_v6 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); + int ret = bind(fd_v6.get(), reinterpret_cast(&addr_v6), + test_addr_v6.addr_len); + if (ret == -1 && errno == EADDRINUSE) { + // Port may have been in use. + ASSERT_LT(i, 100); // Give up after 100 tries. + continue; + } + ASSERT_THAT(ret, SyscallSucceeds()); + + // Verify that binding the v4 loopback with the same port on a v4 socket + // fails. + TestAddress const& test_addr_v4 = V4Loopback(); + sockaddr_storage addr_v4 = test_addr_v4.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port)); + const FileDescriptor fd_v4 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast(&addr_v4), + test_addr_v4.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // No need to try again. + break; + } +} + +TEST_P(SocketMultiProtocolInetLoopbackTest, DualStackV6AnyReservesEverything) { + auto const& param = GetParam(); + + // Bind the v6 any on a dual stack socket. + TestAddress const& test_addr_dual = V6Any(); + sockaddr_storage addr_dual = test_addr_dual.addr; + const FileDescriptor fd_dual = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_dual.family(), param.type, 0)); + ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast(&addr_dual), + test_addr_dual.addr_len), + SyscallSucceeds()); + + // Get the port that we bound. + socklen_t addrlen = test_addr_dual.addr_len; + ASSERT_THAT(getsockname(fd_dual.get(), + reinterpret_cast(&addr_dual), &addrlen), + SyscallSucceeds()); + uint16_t const port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); + + // Verify that binding the v6 loopback with the same port fails. + TestAddress const& test_addr_v6 = V6Loopback(); + sockaddr_storage addr_v6 = test_addr_v6.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); + const FileDescriptor fd_v6 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast(&addr_v6), + test_addr_v6.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v4 loopback on the same port with a v6 socket + // fails. + TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); + sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, port)); + const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v4_mapped.family(), param.type, 0)); + ASSERT_THAT( + bind(fd_v4_mapped.get(), reinterpret_cast(&addr_v4_mapped), + test_addr_v4_mapped.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v4 loopback on the same port with a v4 socket + // fails. + TestAddress const& test_addr_v4 = V4Loopback(); + sockaddr_storage addr_v4 = test_addr_v4.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port)); + const FileDescriptor fd_v4 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast(&addr_v4), + test_addr_v4.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); +} + +TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) { + auto const& param = GetParam(); + + for (int i = 0; true; i++) { + // Bind the v6 any on a v6-only socket. + TestAddress const& test_addr_dual = V6Any(); + sockaddr_storage addr_dual = test_addr_dual.addr; + const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_dual.family(), param.type, 0)); + int one = 1; + EXPECT_THAT( + setsockopt(fd_dual.get(), IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)), + SyscallSucceeds()); + ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast(&addr_dual), + test_addr_dual.addr_len), + SyscallSucceeds()); + + // Get the port that we bound. + socklen_t addrlen = test_addr_dual.addr_len; + ASSERT_THAT(getsockname(fd_dual.get(), + reinterpret_cast(&addr_dual), &addrlen), + SyscallSucceeds()); + uint16_t const port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); + + // Verify that binding the v6 loopback with the same port fails. + TestAddress const& test_addr_v6 = V6Loopback(); + sockaddr_storage addr_v6 = test_addr_v6.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); + const FileDescriptor fd_v6 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast(&addr_v6), + test_addr_v6.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that we can still bind the v4 loopback on the same port. + TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); + sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, port)); + const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v4_mapped.family(), param.type, 0)); + int ret = + bind(fd_v4_mapped.get(), reinterpret_cast(&addr_v4_mapped), + test_addr_v4_mapped.addr_len); + if (ret == -1 && errno == EADDRINUSE) { + // Port may have been in use. + ASSERT_LT(i, 100); // Give up after 100 tries. + continue; + } + ASSERT_THAT(ret, SyscallSucceeds()); + + // No need to try again. + break; + } +} + +TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) { + auto const& param = GetParam(); + + // FIXME + SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM); + + for (int i = 0; true; i++) { + // Bind the v6 loopback on a dual stack socket. + TestAddress const& test_addr = V6Loopback(); + sockaddr_storage bound_addr = test_addr.addr; + const FileDescriptor bound_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast(&bound_addr), + test_addr.addr_len), + SyscallSucceeds()); + + // Listen iff TCP. + if (param.type == SOCK_STREAM) { + ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); + } + + // Get the port that we bound. + socklen_t bound_addr_len = test_addr.addr_len; + ASSERT_THAT( + getsockname(bound_fd.get(), reinterpret_cast(&bound_addr), + &bound_addr_len), + SyscallSucceeds()); + + // Connect to bind an ephemeral port. + const FileDescriptor connected_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + ASSERT_THAT( + connect(connected_fd.get(), reinterpret_cast(&bound_addr), + bound_addr_len), + SyscallSucceeds()); + + // Get the ephemeral port. + sockaddr_storage connected_addr = {}; + socklen_t connected_addr_len = sizeof(connected_addr); + ASSERT_THAT(getsockname(connected_fd.get(), + reinterpret_cast(&connected_addr), + &connected_addr_len), + SyscallSucceeds()); + uint16_t const ephemeral_port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); + + // Verify that we actually got an ephemeral port. + ASSERT_NE(ephemeral_port, 0); + + // Verify that the ephemeral port is reserved. + const FileDescriptor checking_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + EXPECT_THAT( + bind(checking_fd.get(), reinterpret_cast(&connected_addr), + connected_addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v6 loopback with the same port fails. + TestAddress const& test_addr_v6 = V6Loopback(); + sockaddr_storage addr_v6 = test_addr_v6.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port)); + const FileDescriptor fd_v6 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast(&addr_v6), + test_addr_v6.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v4 any with the same port fails. + TestAddress const& test_addr_v4_any = V4Any(); + sockaddr_storage addr_v4_any = test_addr_v4_any.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v4_any.family(), &addr_v4_any, ephemeral_port)); + const FileDescriptor fd_v4_any = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v4_any.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v4_any.get(), reinterpret_cast(&addr_v4_any), + test_addr_v4_any.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that we can still bind the v4 loopback on the same port. + TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); + sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, + ephemeral_port)); + const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v4_mapped.family(), param.type, 0)); + int ret = + bind(fd_v4_mapped.get(), reinterpret_cast(&addr_v4_mapped), + test_addr_v4_mapped.addr_len); + if (ret == -1 && errno == EADDRINUSE) { + // Port may have been in use. + ASSERT_LT(i, 100); // Give up after 100 tries. + continue; + } + EXPECT_THAT(ret, SyscallSucceeds()); + + // No need to try again. + break; + } +} + +TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) { + auto const& param = GetParam(); + + // FIXME + SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM); + + for (int i = 0; true; i++) { + // Bind the v4 loopback on a dual stack socket. + TestAddress const& test_addr = V4MappedLoopback(); + sockaddr_storage bound_addr = test_addr.addr; + const FileDescriptor bound_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast(&bound_addr), + test_addr.addr_len), + SyscallSucceeds()); + + // Listen iff TCP. + if (param.type == SOCK_STREAM) { + ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); + } + + // Get the port that we bound. + socklen_t bound_addr_len = test_addr.addr_len; + ASSERT_THAT( + getsockname(bound_fd.get(), reinterpret_cast(&bound_addr), + &bound_addr_len), + SyscallSucceeds()); + + // Connect to bind an ephemeral port. + const FileDescriptor connected_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + ASSERT_THAT( + connect(connected_fd.get(), reinterpret_cast(&bound_addr), + bound_addr_len), + SyscallSucceeds()); + + // Get the ephemeral port. + sockaddr_storage connected_addr = {}; + socklen_t connected_addr_len = sizeof(connected_addr); + ASSERT_THAT(getsockname(connected_fd.get(), + reinterpret_cast(&connected_addr), + &connected_addr_len), + SyscallSucceeds()); + uint16_t const ephemeral_port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); + + // Verify that we actually got an ephemeral port. + ASSERT_NE(ephemeral_port, 0); + + // Verify that the ephemeral port is reserved. + const FileDescriptor checking_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + EXPECT_THAT( + bind(checking_fd.get(), reinterpret_cast(&connected_addr), + connected_addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v4 loopback on the same port with a v4 socket + // fails. + TestAddress const& test_addr_v4 = V4Loopback(); + sockaddr_storage addr_v4 = test_addr_v4.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v4.family(), &addr_v4, ephemeral_port)); + const FileDescriptor fd_v4 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); + EXPECT_THAT(bind(fd_v4.get(), reinterpret_cast(&addr_v4), + test_addr_v4.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v6 any on the same port with a dual-stack socket + // fails. + TestAddress const& test_addr_v6_any = V6Any(); + sockaddr_storage addr_v6_any = test_addr_v6_any.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v6_any.family(), &addr_v6_any, ephemeral_port)); + const FileDescriptor fd_v6_any = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v6_any.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v6_any.get(), reinterpret_cast(&addr_v6_any), + test_addr_v6_any.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // For some reason, binding the TCP v6-only any is flaky on Linux. Maybe we + // tend to run out of ephemeral ports? Regardless, binding the v6 loopback + // seems pretty reliable. Only try to bind the v6-only any on UDP and + // gVisor. + + int ret = -1; + + if (!IsRunningOnGvisor() && param.type == SOCK_STREAM) { + // Verify that we can still bind the v6 loopback on the same port. + TestAddress const& test_addr_v6 = V6Loopback(); + sockaddr_storage addr_v6 = test_addr_v6.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port)); + const FileDescriptor fd_v6 = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v6.family(), param.type, 0)); + ret = bind(fd_v6.get(), reinterpret_cast(&addr_v6), + test_addr_v6.addr_len); + } else { + // Verify that we can still bind the v6 any on the same port with a + // v6-only socket. + const FileDescriptor fd_v6_only_any = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v6_any.family(), param.type, 0)); + int one = 1; + EXPECT_THAT(setsockopt(fd_v6_only_any.get(), IPPROTO_IPV6, IPV6_V6ONLY, + &one, sizeof(one)), + SyscallSucceeds()); + ret = + bind(fd_v6_only_any.get(), reinterpret_cast(&addr_v6_any), + test_addr_v6_any.addr_len); + } + + if (ret == -1 && errno == EADDRINUSE) { + // Port may have been in use. + ASSERT_LT(i, 100); // Give up after 100 tries. + continue; + } + EXPECT_THAT(ret, SyscallSucceeds()); + + // No need to try again. + break; + } +} + +TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) { + auto const& param = GetParam(); + + // FIXME + SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM); + + for (int i = 0; true; i++) { + // Bind the v4 loopback on a v4 socket. + TestAddress const& test_addr = V4Loopback(); + sockaddr_storage bound_addr = test_addr.addr; + const FileDescriptor bound_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast(&bound_addr), + test_addr.addr_len), + SyscallSucceeds()); + + // Listen iff TCP. + if (param.type == SOCK_STREAM) { + ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); + } + + // Get the port that we bound. + socklen_t bound_addr_len = test_addr.addr_len; + ASSERT_THAT( + getsockname(bound_fd.get(), reinterpret_cast(&bound_addr), + &bound_addr_len), + SyscallSucceeds()); + + // Connect to bind an ephemeral port. + const FileDescriptor connected_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + ASSERT_THAT( + connect(connected_fd.get(), reinterpret_cast(&bound_addr), + bound_addr_len), + SyscallSucceeds()); + + // Get the ephemeral port. + sockaddr_storage connected_addr = {}; + socklen_t connected_addr_len = sizeof(connected_addr); + ASSERT_THAT(getsockname(connected_fd.get(), + reinterpret_cast(&connected_addr), + &connected_addr_len), + SyscallSucceeds()); + uint16_t const ephemeral_port = + ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); + + // Verify that we actually got an ephemeral port. + ASSERT_NE(ephemeral_port, 0); + + // Verify that the ephemeral port is reserved. + const FileDescriptor checking_fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); + EXPECT_THAT( + bind(checking_fd.get(), reinterpret_cast(&connected_addr), + connected_addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v4 loopback on the same port with a v6 socket + // fails. + TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); + sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; + ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, + ephemeral_port)); + const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v4_mapped.family(), param.type, 0)); + EXPECT_THAT( + bind(fd_v4_mapped.get(), reinterpret_cast(&addr_v4_mapped), + test_addr_v4_mapped.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // Verify that binding the v6 any on the same port with a dual-stack socket + // fails. + TestAddress const& test_addr_v6_any = V6Any(); + sockaddr_storage addr_v6_any = test_addr_v6_any.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v6_any.family(), &addr_v6_any, ephemeral_port)); + const FileDescriptor fd_v6_any = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v6_any.family(), param.type, 0)); + ASSERT_THAT(bind(fd_v6_any.get(), reinterpret_cast(&addr_v6_any), + test_addr_v6_any.addr_len), + SyscallFailsWithErrno(EADDRINUSE)); + + // For some reason, binding the TCP v6-only any is flaky on Linux. Maybe we + // tend to run out of ephemeral ports? Regardless, binding the v6 loopback + // seems pretty reliable. Only try to bind the v6-only any on UDP and + // gVisor. + + int ret = -1; + + if (!IsRunningOnGvisor() && param.type == SOCK_STREAM) { + // Verify that we can still bind the v6 loopback on the same port. + TestAddress const& test_addr_v6 = V6Loopback(); + sockaddr_storage addr_v6 = test_addr_v6.addr; + ASSERT_NO_ERRNO( + SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port)); + const FileDescriptor fd_v6 = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v6.family(), param.type, 0)); + ret = bind(fd_v6.get(), reinterpret_cast(&addr_v6), + test_addr_v6.addr_len); + } else { + // Verify that we can still bind the v6 any on the same port with a + // v6-only socket. + const FileDescriptor fd_v6_only_any = ASSERT_NO_ERRNO_AND_VALUE( + Socket(test_addr_v6_any.family(), param.type, 0)); + int one = 1; + EXPECT_THAT(setsockopt(fd_v6_only_any.get(), IPPROTO_IPV6, IPV6_V6ONLY, + &one, sizeof(one)), + SyscallSucceeds()); + ret = + bind(fd_v6_only_any.get(), reinterpret_cast(&addr_v6_any), + test_addr_v6_any.addr_len); + } + + if (ret == -1 && errno == EADDRINUSE) { + // Port may have been in use. + ASSERT_LT(i, 100); // Give up after 100 tries. + continue; + } + EXPECT_THAT(ret, SyscallSucceeds()); + + // No need to try again. + break; + } +} + +INSTANTIATE_TEST_CASE_P(AllFamlies, SocketMultiProtocolInetLoopbackTest, + ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM}, + ProtocolTestParam{"UDP", SOCK_DGRAM}), + DescribeProtocolTestParam); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc new file mode 100644 index 000000000..bb5a83c9a --- /dev/null +++ b/test/syscalls/linux/socket_ip_tcp_generic.cc @@ -0,0 +1,392 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_ip_tcp_generic.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +TEST_P(TCPSocketPairTest, TcpInfoSucceedes) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct tcp_info opt = {}; + socklen_t optLen = sizeof(opt); + EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen), + SyscallSucceeds()); +} + +TEST_P(TCPSocketPairTest, ShortTcpInfoSucceedes) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct tcp_info opt = {}; + socklen_t optLen = 1; + EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen), + SyscallSucceeds()); +} + +TEST_P(TCPSocketPairTest, ZeroTcpInfoSucceedes) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct tcp_info opt = {}; + socklen_t optLen = 0; + EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen), + SyscallSucceeds()); +} + +// This test validates that an RST is sent instead of a FIN when data is +// unread on calls to close(2). +TEST_P(TCPSocketPairTest, RSTSentOnCloseWithUnreadData) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // Wait until t_ sees the data on its side but don't read it. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; + constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Now close the connected without reading the data. + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); + + // Wait for the other end to receive the RST (up to 20 seconds). + struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // A shutdown with unread data will cause a RST to be sent instead + // of a FIN, per RFC 2525 section 2.17; this is also what Linux does. + ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), + SyscallFailsWithErrno(ECONNRESET)); +} + +// This test will validate that a RST will cause POLLHUP to trigger. +TEST_P(TCPSocketPairTest, RSTCausesPollHUP) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // Wait until second sees the data on its side but don't read it. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0}; + constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); + + // Confirm we at least have one unread byte. + int bytes_available = 0; + ASSERT_THAT( + RetryEINTR(ioctl)(sockets->second_fd(), FIONREAD, &bytes_available), + SyscallSucceeds()); + EXPECT_GT(bytes_available, 0); + + // Now close the connected socket without reading the data from the second, + // this will cause a RST and we should see that with POLLHUP. + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); + + // Wait for the other end to receive the RST (up to 20 seconds). + struct pollfd poll_fd3 = {sockets->first_fd(), POLLHUP, 0}; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd3, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + ASSERT_NE(poll_fd.revents & (POLLHUP | POLLIN), 0); +} + +// This test validates that even if a RST is sent the other end will not +// get an ECONNRESET until it's read all data. +TEST_P(TCPSocketPairTest, RSTSentOnCloseWithUnreadDataAllowsReadBuffered) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + ASSERT_THAT(RetryEINTR(write)(sockets->second_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // Wait until second sees the data on its side but don't read it. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0}; + constexpr int kPollTimeoutMs = 30000; // Wait up to 30 seconds for the data. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Wait until first sees the data on its side but don't read it. + struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN, 0}; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Now close the connected socket without reading the data from the second. + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); + + // Wait for the other end to receive the RST (up to 30 seconds). + struct pollfd poll_fd3 = {sockets->first_fd(), POLLHUP, 0}; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd3, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Since we also have data buffered we should be able to read it before + // the syscall will fail with ECONNRESET. + ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // A shutdown with unread data will cause a RST to be sent instead + // of a FIN, per RFC 2525 section 2.17; this is also what Linux does. + ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), + SyscallFailsWithErrno(ECONNRESET)); +} + +// This test will verify that a clean shutdown (FIN) is preformed when there +// is unread data but only the write side is closed. +TEST_P(TCPSocketPairTest, FINSentOnShutdownWrWithUnreadData) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // Wait until t_ sees the data on its side but don't read it. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; + constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Now shutdown the write end leaving the read end open. + ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_WR), SyscallSucceeds()); + + // Wait for the other end to receive the FIN (up to 20 seconds). + struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Since we didn't shutdown the read end this will be a clean close. + ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(0)); +} + +// This test will verify that when data is received by a socket, even if it's +// not read SHUT_RD will not cause any packets to be generated and data will +// remain in the buffer and can be read later. +TEST_P(TCPSocketPairTest, ShutdownRdShouldCauseNoPacketsWithUnreadData) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // Wait until t_ sees the data on its side but don't read it. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; + constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Now shutdown the read end, this will generate no packets to the other end. + ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RD), SyscallSucceeds()); + + // We should not receive any events on the other side of the socket. + struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; + constexpr int kPollNoResponseTimeoutMs = 3000; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollNoResponseTimeoutMs), + SyscallSucceedsWithValue(0)); // Timeout. + + // Even though we did a SHUT_RD on the read end we can still read the data. + ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); +} + +TEST_P(TCPSocketPairTest, ClosedReadNonBlockingSocket) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Set the read end to O_NONBLOCK. + int opts = 0; + ASSERT_THAT(opts = fcntl(sockets->second_fd(), F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(sockets->second_fd(), F_SETFL, opts | O_NONBLOCK), + SyscallSucceeds()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0), + SyscallSucceedsWithValue(sizeof(buf))); + + // Wait until second_fd sees the data and then recv it. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0}; + constexpr int kPollTimeoutMs = 2000; // Wait up to 2 seconds for the data. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0), + SyscallSucceedsWithValue(sizeof(buf))); + + // Now shutdown the write end leaving the read end open. + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + + // Wait for close notification and recv again. + struct pollfd poll_fd2 = {sockets->second_fd(), POLLIN, 0}; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0), + SyscallSucceedsWithValue(0)); +} + +TEST_P(TCPSocketPairTest, + ShutdownRdUnreadDataShouldCauseNoPacketsUnlessClosed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + // Wait until t_ sees the data on its side but don't read it. + struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; + constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. + ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); + + // Now shutdown the read end, this will generate no packets to the other end. + ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RD), SyscallSucceeds()); + + // We should not receive any events on the other side of the socket. + struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; + constexpr int kPollNoResponseTimeoutMs = 3000; + ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollNoResponseTimeoutMs), + SyscallSucceedsWithValue(0)); // Timeout. + + // Now since we've fully closed the connection it will generate a RST. + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); + ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), + SyscallSucceedsWithValue(1)); // The other end has closed. + + // A shutdown with unread data will cause a RST to be sent instead + // of a FIN, per RFC 2525 section 2.17; this is also what Linux does. + ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), + SyscallFailsWithErrno(ECONNRESET)); +} + +TEST_P(TCPSocketPairTest, TCPCorkDefault) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT( + getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, 0); +} + +TEST_P(TCPSocketPairTest, SetTCPCork) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, + &kSockOptOn, sizeof(kSockOptOn)), + SyscallSucceeds()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT( + getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOn); + + ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, + &kSockOptOff, sizeof(kSockOptOff)), + SyscallSucceeds()); + + EXPECT_THAT( + getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOff); +} + +TEST_P(TCPSocketPairTest, TCPCork) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, + &kSockOptOn, sizeof(kSockOptOn)), + SyscallSucceeds()); + + constexpr char kData[] = "abc"; + ASSERT_THAT(WriteFd(sockets->first_fd(), kData, sizeof(kData)), + SyscallSucceedsWithValue(sizeof(kData))); + + ASSERT_NO_FATAL_FAILURE(RecvNoData(sockets->second_fd())); + + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, + &kSockOptOff, sizeof(kSockOptOff)), + SyscallSucceeds()); + + // Create a receive buffer larger than kData. + char buf[(sizeof(kData) + 1) * 2] = {}; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0), + SyscallSucceedsWithValue(sizeof(kData))); + EXPECT_EQ(absl::string_view(kData, sizeof(kData)), + absl::string_view(buf, sizeof(kData))); +} + +TEST_P(TCPSocketPairTest, TCPQuickAckDefault) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get, + &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOn); +} + +TEST_P(TCPSocketPairTest, SetTCPQuickAck) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, + &kSockOptOff, sizeof(kSockOptOff)), + SyscallSucceeds()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get, + &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOff); + + ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, + &kSockOptOn, sizeof(kSockOptOn)), + SyscallSucceeds()); + + EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get, + &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOn); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_generic.h b/test/syscalls/linux/socket_ip_tcp_generic.h new file mode 100644 index 000000000..f38500d14 --- /dev/null +++ b/test/syscalls/linux/socket_ip_tcp_generic.h @@ -0,0 +1,29 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected TCP sockets. +using TCPSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_ diff --git a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc new file mode 100644 index 000000000..9e10dea30 --- /dev/null +++ b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc @@ -0,0 +1,47 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_ip_tcp_generic.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return ApplyVecToVec( + std::vector{ + NoOp, SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &kSockOptOn)}, + VecCat( + ApplyVec( + IPv6TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + IPv4TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + DualStackTCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, TCPSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_loopback.cc b/test/syscalls/linux/socket_ip_tcp_loopback.cc new file mode 100644 index 000000000..f95061506 --- /dev/null +++ b/test/syscalls/linux/socket_ip_tcp_loopback.cc @@ -0,0 +1,43 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_generic.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + IPv6TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + IPv4TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + DualStackTCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc new file mode 100644 index 000000000..bb419e3a8 --- /dev/null +++ b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc @@ -0,0 +1,44 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_stream_blocking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return ApplyVecToVec( + std::vector{ + NoOp, SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &kSockOptOn)}, + VecCat( + ApplyVec( + IPv6TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + IPv4TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, BlockingStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc b/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc new file mode 100644 index 000000000..af6fd635e --- /dev/null +++ b/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc @@ -0,0 +1,46 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_non_blocking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return ApplyVecToVec( + std::vector{ + NoOp, SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &kSockOptOn)}, + VecCat( + ApplyVec( + IPv6TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + IPv4TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonBlockingSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_udp_generic.cc b/test/syscalls/linux/socket_ip_tcp_udp_generic.cc new file mode 100644 index 000000000..91d029985 --- /dev/null +++ b/test/syscalls/linux/socket_ip_tcp_udp_generic.cc @@ -0,0 +1,78 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of TCP and UDP sockets. +using TcpUdpSocketPairTest = SocketPairTest; + +TEST_P(TcpUdpSocketPairTest, ShutdownWrFollowedBySendIsError) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Now shutdown the write end of the first. + ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_WR), SyscallSucceeds()); + + char buf[10] = {}; + ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0), + SyscallFailsWithErrno(EPIPE)); +} + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + IPv6UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK})), + ApplyVec( + IPv4UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK})), + ApplyVec( + DualStackUDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK})), + ApplyVec( + IPv6TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK})), + ApplyVec( + IPv4TCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK})), + ApplyVec( + DualStackTCPAcceptBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK}))); +} + +INSTANTIATE_TEST_CASE_P( + AllTCPSockets, TcpUdpSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_loopback.cc b/test/syscalls/linux/socket_ip_udp_loopback.cc new file mode 100644 index 000000000..8a98fa8df --- /dev/null +++ b/test/syscalls/linux/socket_ip_udp_loopback.cc @@ -0,0 +1,48 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_generic.h" +#include "test/syscalls/linux/socket_non_stream.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + IPv6UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + IPv4UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + DualStackUDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc b/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc new file mode 100644 index 000000000..08ff3e656 --- /dev/null +++ b/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc @@ -0,0 +1,40 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_non_stream_blocking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + IPv6UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC})), + ApplyVec( + IPv4UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, BlockingNonStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc b/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc new file mode 100644 index 000000000..256bcfccf --- /dev/null +++ b/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc @@ -0,0 +1,42 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_non_blocking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + IPv6UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + IPv4UDPBidirectionalBindSocketPair, + AllBitwiseCombinations(List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonBlockingSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc new file mode 100644 index 000000000..7bfb62a6f --- /dev/null +++ b/test/syscalls/linux/socket_netdevice.cc @@ -0,0 +1,182 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/internal/endian.h" +#include "test/syscalls/linux/socket_netlink_util.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +// Tests for netdevice queries. + +namespace gvisor { +namespace testing { + +namespace { + +using ::testing::AnyOf; +using ::testing::Eq; + +TEST(NetdeviceTest, Loopback) { + FileDescriptor sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); + + // Prepare the request. + struct ifreq ifr; + snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); + + // Check for a non-zero interface index. + ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds()); + EXPECT_NE(ifr.ifr_ifindex, 0); + + // Check that the loopback is zero hardware address. + ASSERT_THAT(ioctl(sock.get(), SIOCGIFHWADDR, &ifr), SyscallSucceeds()); + EXPECT_EQ(ifr.ifr_hwaddr.sa_data[0], 0); + EXPECT_EQ(ifr.ifr_hwaddr.sa_data[1], 0); + EXPECT_EQ(ifr.ifr_hwaddr.sa_data[2], 0); + EXPECT_EQ(ifr.ifr_hwaddr.sa_data[3], 0); + EXPECT_EQ(ifr.ifr_hwaddr.sa_data[4], 0); + EXPECT_EQ(ifr.ifr_hwaddr.sa_data[5], 0); +} + +TEST(NetdeviceTest, Netmask) { + // We need an interface index to identify the loopback device. + FileDescriptor sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); + struct ifreq ifr; + snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); + ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds()); + EXPECT_NE(ifr.ifr_ifindex, 0); + + // Use a netlink socket to get the netmask, which we'll then compare to the + // netmask obtained via ioctl. + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket()); + uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); + + struct request { + struct nlmsghdr hdr; + struct rtgenmsg rgm; + }; + + constexpr uint32_t kSeq = 12345; + + struct request req; + req.hdr.nlmsg_len = sizeof(req); + req.hdr.nlmsg_type = RTM_GETADDR; + req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.hdr.nlmsg_seq = kSeq; + req.rgm.rtgen_family = AF_UNSPEC; + + // Iterate through messages until we find the one containing the prefix length + // (i.e. netmask) for the loopback device. + int prefixlen = -1; + ASSERT_NO_ERRNO(NetlinkRequestResponse( + fd, &req, sizeof(req), [&](const struct nlmsghdr *hdr) { + EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE))); + + EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) + << std::hex << hdr->nlmsg_flags; + + EXPECT_EQ(hdr->nlmsg_seq, kSeq); + EXPECT_EQ(hdr->nlmsg_pid, port); + + if (hdr->nlmsg_type != RTM_NEWADDR) { + return; + } + + // RTM_NEWADDR contains at least the header and ifaddrmsg. + EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg)); + + struct ifaddrmsg *ifaddrmsg = + reinterpret_cast(NLMSG_DATA(hdr)); + if (ifaddrmsg->ifa_index == static_cast(ifr.ifr_ifindex) && + ifaddrmsg->ifa_family == AF_INET) { + prefixlen = ifaddrmsg->ifa_prefixlen; + } + })); + + ASSERT_GE(prefixlen, 0); + + // Netmask is stored big endian in struct sockaddr_in, so we do the same for + // comparison. + uint32_t mask = 0xffffffff << (32 - prefixlen); + mask = absl::gbswap_32(mask); + + // Check that the loopback interface has the correct subnet mask. + snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); + ASSERT_THAT(ioctl(sock.get(), SIOCGIFNETMASK, &ifr), SyscallSucceeds()); + EXPECT_EQ(ifr.ifr_netmask.sa_family, AF_INET); + struct sockaddr_in *sin = + reinterpret_cast(&ifr.ifr_netmask); + EXPECT_EQ(sin->sin_addr.s_addr, mask); +} + +TEST(NetdeviceTest, InterfaceName) { + FileDescriptor sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); + + // Prepare the request. + struct ifreq ifr; + snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); + + // Check for a non-zero interface index. + ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds()); + EXPECT_NE(ifr.ifr_ifindex, 0); + + // Check that SIOCGIFNAME finds the loopback interface. + snprintf(ifr.ifr_name, IFNAMSIZ, "foo"); + ASSERT_THAT(ioctl(sock.get(), SIOCGIFNAME, &ifr), SyscallSucceeds()); + EXPECT_STREQ(ifr.ifr_name, "lo"); +} + +TEST(NetdeviceTest, InterfaceFlags) { + FileDescriptor sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); + + // Prepare the request. + struct ifreq ifr; + snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); + + // Check that SIOCGIFFLAGS marks the interface with IFF_LOOPBACK, IFF_UP, and + // IFF_RUNNING. + ASSERT_THAT(ioctl(sock.get(), SIOCGIFFLAGS, &ifr), SyscallSucceeds()); + EXPECT_EQ(ifr.ifr_flags & IFF_UP, IFF_UP); + EXPECT_EQ(ifr.ifr_flags & IFF_RUNNING, IFF_RUNNING); +} + +TEST(NetdeviceTest, InterfaceMTU) { + FileDescriptor sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); + + // Prepare the request. + struct ifreq ifr = {}; + snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); + + // Check that SIOCGIFMTU returns a nonzero MTU. + ASSERT_THAT(ioctl(sock.get(), SIOCGIFMTU, &ifr), SyscallSucceeds()); + EXPECT_GT(ifr.ifr_mtu, 0); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc new file mode 100644 index 000000000..9fc695460 --- /dev/null +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -0,0 +1,314 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_netlink_util.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +// Tests for NETLINK_ROUTE sockets. + +namespace gvisor { +namespace testing { + +namespace { + +using ::testing::AnyOf; +using ::testing::Eq; + +// Netlink sockets must be SOCK_DGRAM or SOCK_RAW. +TEST(NetlinkRouteTest, Types) { + EXPECT_THAT(socket(AF_NETLINK, SOCK_STREAM, NETLINK_ROUTE), + SyscallFailsWithErrno(ESOCKTNOSUPPORT)); + EXPECT_THAT(socket(AF_NETLINK, SOCK_SEQPACKET, NETLINK_ROUTE), + SyscallFailsWithErrno(ESOCKTNOSUPPORT)); + EXPECT_THAT(socket(AF_NETLINK, SOCK_RDM, NETLINK_ROUTE), + SyscallFailsWithErrno(ESOCKTNOSUPPORT)); + EXPECT_THAT(socket(AF_NETLINK, SOCK_DCCP, NETLINK_ROUTE), + SyscallFailsWithErrno(ESOCKTNOSUPPORT)); + EXPECT_THAT(socket(AF_NETLINK, SOCK_PACKET, NETLINK_ROUTE), + SyscallFailsWithErrno(ESOCKTNOSUPPORT)); + + int fd; + EXPECT_THAT(fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + EXPECT_THAT(fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(NetlinkRouteTest, AutomaticPort) { + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + struct sockaddr_nl addr = {}; + addr.nl_family = AF_NETLINK; + + EXPECT_THAT( + bind(fd.get(), reinterpret_cast(&addr), sizeof(addr)), + SyscallSucceeds()); + + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getsockname(fd.get(), reinterpret_cast(&addr), + &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, sizeof(addr)); + // This is the only netlink socket in the process, so it should get the PID as + // the port id. + // + // N.B. Another process could theoretically have explicitly reserved our pid + // as a port ID, but that is very unlikely. + EXPECT_EQ(addr.nl_pid, getpid()); +} + +// Calling connect automatically binds to an automatic port. +TEST(NetlinkRouteTest, ConnectBinds) { + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + struct sockaddr_nl addr = {}; + addr.nl_family = AF_NETLINK; + + EXPECT_THAT(connect(fd.get(), reinterpret_cast(&addr), + sizeof(addr)), + SyscallSucceeds()); + + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getsockname(fd.get(), reinterpret_cast(&addr), + &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, sizeof(addr)); + // This is the only netlink socket in the process, so it should get the PID as + // the port id. + // + // N.B. Another process could theoretically have explicitly reserved our pid + // as a port ID, but that is very unlikely. + EXPECT_EQ(addr.nl_pid, getpid()); + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + // Connecting again is allowed, but keeps the same port. + EXPECT_THAT(connect(fd.get(), reinterpret_cast(&addr), + sizeof(addr)), + SyscallSucceeds()); + + addrlen = sizeof(addr); + EXPECT_THAT(getsockname(fd.get(), reinterpret_cast(&addr), + &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, sizeof(addr)); + EXPECT_EQ(addr.nl_pid, getpid()); +} + +TEST(NetlinkRouteTest, GetPeerName) { + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + struct sockaddr_nl addr = {}; + socklen_t addrlen = sizeof(addr); + + EXPECT_THAT(getpeername(fd.get(), reinterpret_cast(&addr), + &addrlen), + SyscallSucceeds()); + + EXPECT_EQ(addrlen, sizeof(addr)); + EXPECT_EQ(addr.nl_family, AF_NETLINK); + // Peer is the kernel if we didn't connect elsewhere. + EXPECT_EQ(addr.nl_pid, 0); +} + +using IntSockOptTest = ::testing::TestWithParam; + +TEST_P(IntSockOptTest, GetSockOpt) { + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + int res; + socklen_t len = sizeof(res); + + EXPECT_THAT(getsockopt(fd.get(), SOL_SOCKET, GetParam(), &res, &len), + SyscallSucceeds()); + + EXPECT_EQ(len, sizeof(res)); + EXPECT_GT(res, 0); +} + +INSTANTIATE_TEST_CASE_P(NetlinkRouteTest, IntSockOptTest, + ::testing::Values(SO_SNDBUF, SO_RCVBUF)); + +// Validates the reponses to RTM_GETLINK + NLM_F_DUMP. +void CheckGetLinkResponse(const struct nlmsghdr* hdr, int seq, int port) { + EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWLINK), Eq(NLMSG_DONE))); + + EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) + << std::hex << hdr->nlmsg_flags; + + EXPECT_EQ(hdr->nlmsg_seq, seq); + EXPECT_EQ(hdr->nlmsg_pid, port); + + if (hdr->nlmsg_type != RTM_NEWLINK) { + return; + } + + // RTM_NEWLINK contains at least the header and ifinfomsg. + EXPECT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); + + // TODO: Check ifinfomsg contents and following attrs. +} + +TEST(NetlinkRouteTest, GetLinkDump) { + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket()); + uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); + + struct request { + struct nlmsghdr hdr; + struct ifinfomsg ifm; + }; + + constexpr uint32_t kSeq = 12345; + + struct request req = {}; + req.hdr.nlmsg_len = sizeof(req); + req.hdr.nlmsg_type = RTM_GETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.hdr.nlmsg_seq = kSeq; + req.ifm.ifi_family = AF_UNSPEC; + + // Loopback is common among all tests, check that it's found. + bool loopbackFound = false; + ASSERT_NO_ERRNO(NetlinkRequestResponse( + fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) { + CheckGetLinkResponse(hdr, kSeq, port); + if (hdr->nlmsg_type != RTM_NEWLINK) { + return; + } + ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); + const struct ifinfomsg* msg = + reinterpret_cast(NLMSG_DATA(hdr)); + LOG(INFO) << "Found interface idx=" << msg->ifi_index + << ", type=" << std::hex << msg->ifi_type; + if (msg->ifi_type == ARPHRD_LOOPBACK) { + loopbackFound = true; + EXPECT_NE(msg->ifi_flags & IFF_LOOPBACK, 0); + } + })); + EXPECT_TRUE(loopbackFound); +} + +TEST(NetlinkRouteTest, ControlMessageIgnored) { + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket()); + uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); + + struct request { + struct nlmsghdr control_hdr; + struct nlmsghdr message_hdr; + struct ifinfomsg ifm; + }; + + constexpr uint32_t kSeq = 12345; + + struct request req = {}; + + // This control message is ignored. We still receive a response for the + // following RTM_GETLINK. + req.control_hdr.nlmsg_len = sizeof(req.control_hdr); + req.control_hdr.nlmsg_type = NLMSG_DONE; + req.control_hdr.nlmsg_seq = kSeq; + + req.message_hdr.nlmsg_len = sizeof(req.message_hdr) + sizeof(req.ifm); + req.message_hdr.nlmsg_type = RTM_GETLINK; + req.message_hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.message_hdr.nlmsg_seq = kSeq; + + req.ifm.ifi_family = AF_UNSPEC; + + ASSERT_NO_ERRNO(NetlinkRequestResponse( + fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) { + CheckGetLinkResponse(hdr, kSeq, port); + })); +} + +TEST(NetlinkRouteTest, GetAddrDump) { + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket()); + uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); + + struct request { + struct nlmsghdr hdr; + struct rtgenmsg rgm; + }; + + constexpr uint32_t kSeq = 12345; + + struct request req; + req.hdr.nlmsg_len = sizeof(req); + req.hdr.nlmsg_type = RTM_GETADDR; + req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.hdr.nlmsg_seq = kSeq; + req.rgm.rtgen_family = AF_UNSPEC; + + ASSERT_NO_ERRNO(NetlinkRequestResponse( + fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) { + EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE))); + + EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) + << std::hex << hdr->nlmsg_flags; + + EXPECT_EQ(hdr->nlmsg_seq, kSeq); + EXPECT_EQ(hdr->nlmsg_pid, port); + + if (hdr->nlmsg_type != RTM_NEWADDR) { + return; + } + + // RTM_NEWADDR contains at least the header and ifaddrmsg. + EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg)); + + // TODO: Check ifaddrmsg contents and following attrs. + })); +} + +TEST(NetlinkRouteTest, LookupAll) { + struct ifaddrs* if_addr_list = nullptr; + auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); }); + + // Not a syscall but we can use the syscall matcher as glibc sets errno. + ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds()); + + int count = 0; + for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) { + if (!i->ifa_addr || (i->ifa_addr->sa_family != AF_INET && + i->ifa_addr->sa_family != AF_INET6)) { + continue; + } + count++; + } + ASSERT_GT(count, 0); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc new file mode 100644 index 000000000..ee0e03966 --- /dev/null +++ b/test/syscalls/linux/socket_netlink_util.cc @@ -0,0 +1,100 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include +#include + +#include "absl/strings/str_cat.h" +#include "test/syscalls/linux/socket_netlink_util.h" +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +PosixErrorOr NetlinkBoundSocket() { + FileDescriptor fd; + ASSIGN_OR_RETURN_ERRNO(fd, Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); + + struct sockaddr_nl addr = {}; + addr.nl_family = AF_NETLINK; + + RETURN_ERROR_IF_SYSCALL_FAIL( + bind(fd.get(), reinterpret_cast(&addr), sizeof(addr))); + MaybeSave(); + + return std::move(fd); +} + +PosixErrorOr NetlinkPortID(int fd) { + struct sockaddr_nl addr; + socklen_t addrlen = sizeof(addr); + + RETURN_ERROR_IF_SYSCALL_FAIL( + getsockname(fd, reinterpret_cast(&addr), &addrlen)); + MaybeSave(); + + return static_cast(addr.nl_pid); +} + +PosixError NetlinkRequestResponse( + const FileDescriptor& fd, void* request, size_t len, + const std::function& fn) { + struct iovec iov = {}; + iov.iov_base = request; + iov.iov_len = len; + + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + // No destination required; it defaults to pid 0, the kernel. + + RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(sendmsg)(fd.get(), &msg, 0)); + + constexpr size_t kBufferSize = 4096; + std::vector buf(kBufferSize); + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + + // Response is a series of NLM_F_MULTI messages, ending with a NLMSG_DONE + // message. + int type = -1; + do { + int len; + RETURN_ERROR_IF_SYSCALL_FAIL(len = RetryEINTR(recvmsg)(fd.get(), &msg, 0)); + + // We don't bother with the complexity of dealing with truncated messages. + // We must allocate a large enough buffer up front. + if ((msg.msg_flags & MSG_TRUNC) == MSG_TRUNC) { + return PosixError(EIO, + absl::StrCat("Received truncated message with flags: ", + msg.msg_flags)); + } + + for (struct nlmsghdr* hdr = reinterpret_cast(buf.data()); + NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { + fn(hdr); + type = hdr->nlmsg_type; + } + } while (type != NLMSG_DONE && type != NLMSG_ERROR); + + EXPECT_EQ(type, NLMSG_DONE); + return NoError(); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_util.h b/test/syscalls/linux/socket_netlink_util.h new file mode 100644 index 000000000..44b1f148c --- /dev/null +++ b/test/syscalls/linux/socket_netlink_util.h @@ -0,0 +1,42 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_SOCKET_NETLINK_UTIL_H_ +#define GVISOR_TEST_SYSCALLS_SOCKET_NETLINK_UTIL_H_ + +#include +#include +#include + +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { + +// Returns a bound NETLINK_ROUTE socket. +PosixErrorOr NetlinkBoundSocket(); + +// Returns the port ID of the passed socket. +PosixErrorOr NetlinkPortID(int fd); + +// Send the passed request and call fn will all response netlink messages. +PosixError NetlinkRequestResponse( + const FileDescriptor& fd, void* request, size_t len, + const std::function& fn); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_SOCKET_NETLINK_UTIL_H_ diff --git a/test/syscalls/linux/socket_non_blocking.cc b/test/syscalls/linux/socket_non_blocking.cc new file mode 100644 index 000000000..1bcc6fb7f --- /dev/null +++ b/test/syscalls/linux/socket_non_blocking.cc @@ -0,0 +1,63 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_non_blocking.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +TEST_P(NonBlockingSocketPairTest, ReadNothingAvailable) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char buf[20] = {}; + ASSERT_THAT(ReadFd(sockets->first_fd(), buf, sizeof(buf)), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(NonBlockingSocketPairTest, RecvNothingAvailable) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char buf[20] = {}; + ASSERT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(NonBlockingSocketPairTest, RecvMsgNothingAvailable) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct iovec iov; + char buf[20] = {}; + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0), + SyscallFailsWithErrno(EAGAIN)); +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_non_blocking.h b/test/syscalls/linux/socket_non_blocking.h new file mode 100644 index 000000000..287e096bb --- /dev/null +++ b/test/syscalls/linux/socket_non_blocking.h @@ -0,0 +1,29 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected non-blocking sockets. +using NonBlockingSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_ diff --git a/test/syscalls/linux/socket_non_stream.cc b/test/syscalls/linux/socket_non_stream.cc new file mode 100644 index 000000000..d49aab363 --- /dev/null +++ b/test/syscalls/linux/socket_non_stream.cc @@ -0,0 +1,174 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_non_stream.h" + +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +TEST_P(NonStreamSocketPairTest, SendMsgTooLarge) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int sndbuf; + socklen_t length = sizeof(sndbuf); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &sndbuf, &length), + SyscallSucceeds()); + + // Make the call too large to fit in the send buffer. + const int buffer_size = 3 * sndbuf; + + EXPECT_THAT(SendLargeSendMsg(sockets, buffer_size, false /* reader */), + SyscallFailsWithErrno(EMSGSIZE)); +} + +// Stream sockets allow data sent with a single (e.g. write, sendmsg) syscall +// to be read in pieces with multiple (e.g. read, recvmsg) syscalls. +// +// SplitRecv checks that control messages can only be read on the first (e.g. +// read, recvmsg) syscall, even if it doesn't provide space for the control +// message. +TEST_P(NonStreamSocketPairTest, SplitRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data) / 2]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); +} + +// Stream sockets allow data sent with multiple sends to be read in a single +// recv. Datagram sockets do not. +// +// SingleRecv checks that only a single message is readable in a single recv. +TEST_P(NonStreamSocketPairTest, SingleRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data1, sizeof(sent_data1), 0), + SyscallSucceedsWithValue(sizeof(sent_data1))); + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data2, sizeof(sent_data2), 0), + SyscallSucceedsWithValue(sizeof(sent_data2))); + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); +} + +// Stream sockets allow data sent with multiple sends to be peeked at in a +// single recv. Datagram sockets (except for unix sockets) do not. +// +// SinglePeek checks that only a single message is peekable in a single recv. +TEST_P(NonStreamSocketPairTest, SinglePeek) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data1, sizeof(sent_data1), 0), + SyscallSucceedsWithValue(sizeof(sent_data1))); + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data2, sizeof(sent_data2), 0), + SyscallSucceedsWithValue(sizeof(sent_data2))); + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + for (int i = 0; i < 3; i++) { + memset(received_data, 0, sizeof(received_data)); + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_PEEK), + SyscallSucceedsWithValue(sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + } + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(sent_data1), 0), + SyscallSucceedsWithValue(sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(sent_data2), 0), + SyscallSucceedsWithValue(sizeof(sent_data2))); + EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(sent_data2))); +} + +TEST_P(NonStreamSocketPairTest, MsgTruncTruncation) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)] = {}; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data) / 2, MSG_TRUNC), + SyscallSucceedsWithValue(sizeof(sent_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); + + // Check that we didn't get any extra data. + EXPECT_NE(0, memcmp(sent_data + sizeof(sent_data) / 2, + received_data + sizeof(received_data) / 2, + sizeof(sent_data) / 2)); +} + +TEST_P(NonStreamSocketPairTest, MsgTruncSameSize) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_TRUNC), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(NonStreamSocketPairTest, MsgTruncNotFull) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[2 * sizeof(sent_data)]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_TRUNC), + SyscallSucceedsWithValue(sizeof(sent_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_non_stream.h b/test/syscalls/linux/socket_non_stream.h new file mode 100644 index 000000000..02dd2a958 --- /dev/null +++ b/test/syscalls/linux/socket_non_stream.h @@ -0,0 +1,29 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected non-stream sockets. +using NonStreamSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_ diff --git a/test/syscalls/linux/socket_non_stream_blocking.cc b/test/syscalls/linux/socket_non_stream_blocking.cc new file mode 100644 index 000000000..d64b181c9 --- /dev/null +++ b/test/syscalls/linux/socket_non_stream_blocking.cc @@ -0,0 +1,51 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_non_stream_blocking.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +TEST_P(BlockingNonStreamSocketPairTest, RecvLessThanBufferWaitAll) { + SKIP_IF(IsRunningOnGvisor()); // FIXME: Support MSG_WAITALL. + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[100]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[sizeof(sent_data) * 2] = {}; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_WAITALL), + SyscallSucceedsWithValue(sizeof(sent_data))); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_non_stream_blocking.h b/test/syscalls/linux/socket_non_stream_blocking.h new file mode 100644 index 000000000..bde355452 --- /dev/null +++ b/test/syscalls/linux/socket_non_stream_blocking.h @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of blocking connected non-stream +// sockets. +using BlockingNonStreamSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_ diff --git a/test/syscalls/linux/socket_stream.cc b/test/syscalls/linux/socket_stream.cc new file mode 100644 index 000000000..32e9d958b --- /dev/null +++ b/test/syscalls/linux/socket_stream.cc @@ -0,0 +1,99 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_stream.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +TEST_P(StreamSocketPairTest, SplitRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data) / 2]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data + sizeof(received_data), received_data, + sizeof(received_data))); +} + +// Stream sockets allow data sent with multiple sends to be read in a single +// recv. +// +// CoalescedRecv checks that multiple messages are readable in a single recv. +TEST_P(StreamSocketPairTest, CoalescedRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data1, sizeof(sent_data1), 0), + SyscallSucceedsWithValue(sizeof(sent_data1))); + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data2, sizeof(sent_data2), 0), + SyscallSucceedsWithValue(sizeof(sent_data2))); + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), + sizeof(sent_data2))); +} + +TEST_P(StreamSocketPairTest, WriteOneSideClosed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + const char str[] = "abc"; + ASSERT_THAT(write(sockets->second_fd(), str, 3), + SyscallFailsWithErrno(EPIPE)); +} + +TEST_P(StreamSocketPairTest, MsgTrunc) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)]; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data) / 2, MSG_TRUNC), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_stream.h b/test/syscalls/linux/socket_stream.h new file mode 100644 index 000000000..35e591e17 --- /dev/null +++ b/test/syscalls/linux/socket_stream.h @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of blocking and non-blocking +// connected stream sockets. +using StreamSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_ diff --git a/test/syscalls/linux/socket_stream_blocking.cc b/test/syscalls/linux/socket_stream_blocking.cc new file mode 100644 index 000000000..dd209c67c --- /dev/null +++ b/test/syscalls/linux/socket_stream_blocking.cc @@ -0,0 +1,131 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_stream_blocking.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +TEST_P(BlockingStreamSocketPairTest, BlockPartialWriteClosed) { + // FIXME: gVisor doesn't support SO_SNDBUF on UDS, nor does it + // enforce any limit; it will write arbitrary amounts of data without + // blocking. + SKIP_IF(IsRunningOnGvisor()); + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int buffer_size; + socklen_t length = sizeof(buffer_size); + ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, + &buffer_size, &length), + SyscallSucceeds()); + + int wfd = sockets->first_fd(); + ScopedThread t([wfd, buffer_size]() { + std::vector buf(2 * buffer_size); + // Write more than fits in the buffer. Blocks then returns partial write + // when the other end is closed. The next call returns EPIPE. + // + // N.B. writes occur in chunks, so we may see less than buffer_size from + // the first call. + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(::testing::Gt(0))); + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + ::testing::AnyOf(SyscallFailsWithErrno(EPIPE), + SyscallFailsWithErrno(ECONNRESET))); + }); + + // Leave time for write to become blocked. + absl::SleepFor(absl::Seconds(1.0)); + + ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); +} + +TEST_P(BlockingStreamSocketPairTest, SendMsgTooLarge) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int sndbuf; + socklen_t length = sizeof(sndbuf); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &sndbuf, &length), + SyscallSucceeds()); + + // Make the call too large to fit in the send buffer. + const int buffer_size = 3 * sndbuf; + + EXPECT_THAT(SendLargeSendMsg(sockets, buffer_size, true /* reader */), + SyscallSucceedsWithValue(buffer_size)); +} + +TEST_P(BlockingStreamSocketPairTest, RecvLessThanBuffer) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[100]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[200] = {}; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); +} + +TEST_P(BlockingStreamSocketPairTest, RecvLessThanBufferWaitAll) { + SKIP_IF(IsRunningOnGvisor()); // FIXME: Support MSG_WAITALL. + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[100]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + constexpr auto kDuration = absl::Milliseconds(200); + auto before = Now(CLOCK_MONOTONIC); + + const ScopedThread t([&]() { + absl::SleepFor(kDuration); + ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + }); + + char received_data[sizeof(sent_data) * 2] = {}; + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_WAITALL), + SyscallSucceedsWithValue(sizeof(received_data))); + + auto after = Now(CLOCK_MONOTONIC); + EXPECT_GE(after - before, kDuration); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_stream_blocking.h b/test/syscalls/linux/socket_stream_blocking.h new file mode 100644 index 000000000..06113ad03 --- /dev/null +++ b/test/syscalls/linux/socket_stream_blocking.h @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of blocking connected stream +// sockets. +using BlockingStreamSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_ diff --git a/test/syscalls/linux/socket_stream_nonblock.cc b/test/syscalls/linux/socket_stream_nonblock.cc new file mode 100644 index 000000000..a3202ffe4 --- /dev/null +++ b/test/syscalls/linux/socket_stream_nonblock.cc @@ -0,0 +1,50 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_stream_nonblock.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +using ::testing::Le; + +TEST_P(NonBlockingStreamSocketPairTest, SendMsgTooLarge) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int sndbuf; + socklen_t length = sizeof(sndbuf); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &sndbuf, &length), + SyscallSucceeds()); + + // Make the call too large to fit in the send buffer. + const int buffer_size = 3 * sndbuf; + + EXPECT_THAT(SendLargeSendMsg(sockets, buffer_size, false /* reader */), + SyscallSucceedsWithValue(Le(buffer_size))); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_stream_nonblock.h b/test/syscalls/linux/socket_stream_nonblock.h new file mode 100644 index 000000000..491f53848 --- /dev/null +++ b/test/syscalls/linux/socket_stream_nonblock.h @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of non-blocking connected stream +// sockets. +using NonBlockingStreamSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_ diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc new file mode 100644 index 000000000..80a59df7e --- /dev/null +++ b/test/syscalls/linux/socket_test_util.cc @@ -0,0 +1,660 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_test_util.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/time/clock.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +Creator SyscallSocketPairCreator(int domain, int type, + int protocol) { + return [=]() -> PosixErrorOr> { + int pair[2]; + RETURN_ERROR_IF_SYSCALL_FAIL(socketpair(domain, type, protocol, pair)); + MaybeSave(); // Save on successful creation. + return absl::make_unique(pair[0], pair[1]); + }; +} + +Creator SyscallSocketCreator(int domain, int type, + int protocol) { + return [=]() -> PosixErrorOr> { + int fd = 0; + RETURN_ERROR_IF_SYSCALL_FAIL(fd = socket(domain, type, protocol)); + MaybeSave(); // Save on successful creation. + return absl::make_unique(fd); + }; +} + +PosixErrorOr UniqueUnixAddr(bool abstract, int domain) { + struct sockaddr_un addr = {}; + std::string path = NewTempAbsPathInDir("/tmp"); + if (path.size() >= sizeof(addr.sun_path)) { + return PosixError(EINVAL, + "Unable to generate a temp path of appropriate length"); + } + + if (abstract) { + // Indicate that the path is in the abstract namespace. + path[0] = 0; + } + memcpy(addr.sun_path, path.c_str(), path.length()); + addr.sun_family = domain; + return addr; +} + +Creator AcceptBindSocketPairCreator(bool abstract, int domain, + int type, int protocol) { + return [=]() -> PosixErrorOr> { + ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un bind_addr, + UniqueUnixAddr(abstract, domain)); + ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un extra_addr, + UniqueUnixAddr(abstract, domain)); + + int bound; + RETURN_ERROR_IF_SYSCALL_FAIL(bound = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + RETURN_ERROR_IF_SYSCALL_FAIL( + bind(bound, reinterpret_cast(&bind_addr), + sizeof(bind_addr))); + MaybeSave(); // Successful bind. + RETURN_ERROR_IF_SYSCALL_FAIL(listen(bound, /* backlog = */ 5)); + MaybeSave(); // Successful listen. + + int connected; + RETURN_ERROR_IF_SYSCALL_FAIL(connected = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + RETURN_ERROR_IF_SYSCALL_FAIL( + connect(connected, reinterpret_cast(&bind_addr), + sizeof(bind_addr))); + MaybeSave(); // Successful connect. + + int accepted; + RETURN_ERROR_IF_SYSCALL_FAIL( + accepted = accept4(bound, nullptr, nullptr, + type & (SOCK_NONBLOCK | SOCK_CLOEXEC))); + MaybeSave(); // Successful connect. + + // Cleanup no longer needed resources. + RETURN_ERROR_IF_SYSCALL_FAIL(close(bound)); + MaybeSave(); // Dropped original socket. + + // Only unlink if path is not in abstract namespace. + if (bind_addr.sun_path[0] != 0) { + RETURN_ERROR_IF_SYSCALL_FAIL(unlink(bind_addr.sun_path)); + MaybeSave(); // Unlinked path. + } + + return absl::make_unique(connected, accepted, bind_addr, + extra_addr); + }; +} + +Creator FilesystemAcceptBindSocketPairCreator(int domain, int type, + int protocol) { + return AcceptBindSocketPairCreator(/* abstract= */ false, domain, type, + protocol); +} + +Creator AbstractAcceptBindSocketPairCreator(int domain, int type, + int protocol) { + return AcceptBindSocketPairCreator(/* abstract= */ true, domain, type, + protocol); +} + +Creator BidirectionalBindSocketPairCreator(bool abstract, + int domain, int type, + int protocol) { + return [=]() -> PosixErrorOr> { + ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr1, + UniqueUnixAddr(abstract, domain)); + ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr2, + UniqueUnixAddr(abstract, domain)); + + int sock1; + RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + RETURN_ERROR_IF_SYSCALL_FAIL( + bind(sock1, reinterpret_cast(&addr1), sizeof(addr1))); + MaybeSave(); // Successful bind. + + int sock2; + RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + RETURN_ERROR_IF_SYSCALL_FAIL( + bind(sock2, reinterpret_cast(&addr2), sizeof(addr2))); + MaybeSave(); // Successful bind. + + RETURN_ERROR_IF_SYSCALL_FAIL(connect( + sock1, reinterpret_cast(&addr2), sizeof(addr2))); + MaybeSave(); // Successful connect. + + RETURN_ERROR_IF_SYSCALL_FAIL(connect( + sock2, reinterpret_cast(&addr1), sizeof(addr1))); + MaybeSave(); // Successful connect. + + // Cleanup no longer needed resources. + + // Only unlink if path is not in abstract namespace. + if (addr1.sun_path[0] != 0) { + RETURN_ERROR_IF_SYSCALL_FAIL(unlink(addr1.sun_path)); + MaybeSave(); // Successful unlink. + } + + // Only unlink if path is not in abstract namespace. + if (addr2.sun_path[0] != 0) { + RETURN_ERROR_IF_SYSCALL_FAIL(unlink(addr2.sun_path)); + MaybeSave(); // Successful unlink. + } + + return absl::make_unique(sock1, sock2); + }; +} + +Creator FilesystemBidirectionalBindSocketPairCreator(int domain, + int type, + int protocol) { + return BidirectionalBindSocketPairCreator(/* abstract= */ false, domain, type, + protocol); +} + +Creator AbstractBidirectionalBindSocketPairCreator(int domain, + int type, + int protocol) { + return BidirectionalBindSocketPairCreator(/* abstract= */ true, domain, type, + protocol); +} + +Creator SocketpairGoferSocketPairCreator(int domain, int type, + int protocol) { + return [=]() -> PosixErrorOr> { + struct sockaddr_un addr = {}; + constexpr char kSocketGoferPath[] = "/socket"; + memcpy(addr.sun_path, kSocketGoferPath, sizeof(kSocketGoferPath)); + addr.sun_family = domain; + + int sock1; + RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + RETURN_ERROR_IF_SYSCALL_FAIL(connect( + sock1, reinterpret_cast(&addr), sizeof(addr))); + MaybeSave(); // Successful connect. + + int sock2; + RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + RETURN_ERROR_IF_SYSCALL_FAIL(connect( + sock2, reinterpret_cast(&addr), sizeof(addr))); + MaybeSave(); // Successful connect. + + // Make and close another socketpair to ensure that the duped ends of the + // first socketpair get closed. + // + // The problem is that there is no way to atomically send and close an FD. + // The closest that we can do is send and then immediately close the FD, + // which is what we do in the gofer. The gofer won't respond to another + // request until the reply is sent and the FD is closed, so forcing the + // gofer to handle another request will ensure that this has happened. + for (int i = 0; i < 2; i++) { + int sock; + RETURN_ERROR_IF_SYSCALL_FAIL(sock = socket(domain, type, protocol)); + RETURN_ERROR_IF_SYSCALL_FAIL(connect( + sock, reinterpret_cast(&addr), sizeof(addr))); + RETURN_ERROR_IF_SYSCALL_FAIL(close(sock)); + } + + return absl::make_unique(sock1, sock2); + }; +} + +Creator SocketpairGoferFileSocketPairCreator(int flags) { + return [=]() -> PosixErrorOr> { + constexpr char kSocketGoferPath[] = "/socket"; + + int sock1; + RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = + open(kSocketGoferPath, O_RDWR | flags)); + MaybeSave(); // Successful socket creation. + + int sock2; + RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = + open(kSocketGoferPath, O_RDWR | flags)); + MaybeSave(); // Successful socket creation. + + return absl::make_unique(sock1, sock2); + }; +} + +Creator UnboundSocketPairCreator(bool abstract, int domain, + int type, int protocol) { + return [=]() -> PosixErrorOr> { + ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr1, + UniqueUnixAddr(abstract, domain)); + ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr2, + UniqueUnixAddr(abstract, domain)); + + int sock1; + RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + int sock2; + RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + return absl::make_unique(sock1, sock2, addr1, addr2); + }; +} + +Creator FilesystemUnboundSocketPairCreator(int domain, int type, + int protocol) { + return UnboundSocketPairCreator(/* abstract= */ false, domain, type, + protocol); +} + +Creator AbstractUnboundSocketPairCreator(int domain, int type, + int protocol) { + return UnboundSocketPairCreator(/* abstract= */ true, domain, type, protocol); +} + +void LocalhostAddr(struct sockaddr_in* addr, bool dual_stack) { + addr->sin_family = AF_INET; + addr->sin_port = htons(0); + inet_pton(AF_INET, "127.0.0.1", + reinterpret_cast(&addr->sin_addr.s_addr)); +} + +void LocalhostAddr(struct sockaddr_in6* addr, bool dual_stack) { + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(0); + if (dual_stack) { + inet_pton(AF_INET6, "::ffff:127.0.0.1", + reinterpret_cast(&addr->sin6_addr.s6_addr)); + } else { + inet_pton(AF_INET6, "::1", + reinterpret_cast(&addr->sin6_addr.s6_addr)); + } + addr->sin6_scope_id = 0; +} + +template +PosixErrorOr BindIP(int fd, bool dual_stack) { + T addr = {}; + LocalhostAddr(&addr, dual_stack); + RETURN_ERROR_IF_SYSCALL_FAIL( + bind(fd, reinterpret_cast(&addr), sizeof(addr))); + socklen_t addrlen = sizeof(addr); + RETURN_ERROR_IF_SYSCALL_FAIL( + getsockname(fd, reinterpret_cast(&addr), &addrlen)); + return addr; +} + +template +PosixErrorOr> CreateTCPAcceptBindSocketPair( + int bound, int connected, int type, bool dual_stack) { + ASSIGN_OR_RETURN_ERRNO(T bind_addr, BindIP(bound, dual_stack)); + RETURN_ERROR_IF_SYSCALL_FAIL(listen(bound, /* backlog = */ 5)); + + int connect_result = 0; + RETURN_ERROR_IF_SYSCALL_FAIL( + (connect_result = RetryEINTR(connect)( + connected, reinterpret_cast(&bind_addr), + sizeof(bind_addr))) == -1 && + errno == EINPROGRESS + ? 0 + : connect_result); + MaybeSave(); // Successful connect. + + if (connect_result == -1) { + struct pollfd connect_poll = {connected, POLLOUT | POLLERR | POLLHUP, 0}; + RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(poll)(&connect_poll, 1, 0)); + int error = 0; + socklen_t errorlen = sizeof(error); + RETURN_ERROR_IF_SYSCALL_FAIL( + getsockopt(connected, SOL_SOCKET, SO_ERROR, &error, &errorlen)); + errno = error; + RETURN_ERROR_IF_SYSCALL_FAIL( + /* connect */ error == 0 ? 0 : -1); + } + + int accepted = -1; + struct pollfd accept_poll = {bound, POLLIN, 0}; + while (accepted == -1) { + RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(poll)(&accept_poll, 1, 0)); + + RETURN_ERROR_IF_SYSCALL_FAIL( + (accepted = RetryEINTR(accept4)( + bound, nullptr, nullptr, type & (SOCK_NONBLOCK | SOCK_CLOEXEC))) == + -1 && + errno == EAGAIN + ? 0 + : accepted); + } + MaybeSave(); // Successful accept. + + // FIXME + if (connect_result == -1) { + absl::SleepFor(absl::Seconds(1)); + } + + // Cleanup no longer needed resources. + RETURN_ERROR_IF_SYSCALL_FAIL(close(bound)); + MaybeSave(); // Successful close. + + T extra_addr = {}; + LocalhostAddr(&extra_addr, dual_stack); + return absl::make_unique(connected, accepted, bind_addr, + extra_addr); +} + +Creator TCPAcceptBindSocketPairCreator(int domain, int type, + int protocol, + bool dual_stack) { + return [=]() -> PosixErrorOr> { + int bound; + RETURN_ERROR_IF_SYSCALL_FAIL(bound = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + + int connected; + RETURN_ERROR_IF_SYSCALL_FAIL(connected = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + + if (domain == AF_INET) { + return CreateTCPAcceptBindSocketPair(bound, connected, type, + dual_stack); + } + return CreateTCPAcceptBindSocketPair(bound, connected, type, + dual_stack); + }; +} + +template +PosixErrorOr> +CreateUDPBidirectionalBindSocketPair(int sock1, int sock2, int type, + bool dual_stack) { + ASSIGN_OR_RETURN_ERRNO(T addr1, BindIP(sock1, dual_stack)); + ASSIGN_OR_RETURN_ERRNO(T addr2, BindIP(sock2, dual_stack)); + + // Connect sock1 to sock2. + RETURN_ERROR_IF_SYSCALL_FAIL(connect( + sock1, reinterpret_cast(&addr2), sizeof(addr2))); + MaybeSave(); // Successful connection. + + // Connect sock2 to sock1. + RETURN_ERROR_IF_SYSCALL_FAIL(connect( + sock2, reinterpret_cast(&addr1), sizeof(addr1))); + MaybeSave(); // Successful connection. + + return absl::make_unique(sock1, sock2, addr1, addr2); +} + +Creator UDPBidirectionalBindSocketPairCreator(int domain, int type, + int protocol, + bool dual_stack) { + return [=]() -> PosixErrorOr> { + int sock1; + RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + + int sock2; + RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + + if (domain == AF_INET) { + return CreateUDPBidirectionalBindSocketPair( + sock1, sock2, type, dual_stack); + } + return CreateUDPBidirectionalBindSocketPair(sock1, sock2, + type, dual_stack); + }; +} + +SocketPairKind Reversed(SocketPairKind const& base) { + auto const& creator = base.creator; + return SocketPairKind{ + absl::StrCat("reversed ", base.description), + [creator]() -> PosixErrorOr> { + ASSIGN_OR_RETURN_ERRNO(auto creator_value, creator()); + return absl::make_unique(std::move(creator_value)); + }}; +} + +std::vector IncludeReversals(std::vector vec) { + return ApplyVecToVec(std::vector{NoOp, Reversed}, + vec); +} + +SocketPairKind NoOp(SocketPairKind const& base) { return base; } + +void TransferTest(int fd1, int fd2) { + char buf1[20]; + RandomizeBuffer(buf1, sizeof(buf1)); + ASSERT_THAT(WriteFd(fd1, buf1, sizeof(buf1)), + SyscallSucceedsWithValue(sizeof(buf1))); + + char buf2[20]; + ASSERT_THAT(ReadFd(fd2, buf2, sizeof(buf2)), + SyscallSucceedsWithValue(sizeof(buf2))); + + EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); + + RandomizeBuffer(buf1, sizeof(buf1)); + ASSERT_THAT(WriteFd(fd2, buf1, sizeof(buf1)), + SyscallSucceedsWithValue(sizeof(buf1))); + + ASSERT_THAT(ReadFd(fd1, buf2, sizeof(buf2)), + SyscallSucceedsWithValue(sizeof(buf2))); + + EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); +} + +// Initializes the given buffer with random data. +void RandomizeBuffer(char* ptr, size_t len) { + uint32_t seed = time(nullptr); + for (size_t i = 0; i < len; ++i) { + ptr[i] = static_cast(rand_r(&seed)); + } +} + +size_t CalculateUnixSockAddrLen(const char* sun_path) { + // Abstract addresses always return the full length. + if (sun_path[0] == 0) { + return sizeof(sockaddr_un); + } + // Filesystem addresses use the address length plus the 2 byte sun_family and + // null terminator. + return strlen(sun_path) + 3; +} + +struct sockaddr_storage AddrFDSocketPair::to_storage(const sockaddr_un& addr) { + struct sockaddr_storage addr_storage = {}; + memcpy(&addr_storage, &addr, sizeof(addr)); + return addr_storage; +} + +struct sockaddr_storage AddrFDSocketPair::to_storage(const sockaddr_in& addr) { + struct sockaddr_storage addr_storage = {}; + memcpy(&addr_storage, &addr, sizeof(addr)); + return addr_storage; +} + +struct sockaddr_storage AddrFDSocketPair::to_storage(const sockaddr_in6& addr) { + struct sockaddr_storage addr_storage = {}; + memcpy(&addr_storage, &addr, sizeof(addr)); + return addr_storage; +} + +SocketKind SimpleSocket(int fam, int type, int proto) { + return SocketKind{ + absl::StrCat("Family ", fam, ", type ", type, ", proto ", proto), + SyscallSocketCreator(fam, type, proto)}; +} + +ssize_t SendLargeSendMsg(const std::unique_ptr& sockets, + size_t size, bool reader) { + const int rfd = sockets->second_fd(); + ScopedThread t([rfd, size, reader] { + if (!reader) { + return; + } + + // Potentially too many syscalls in the loop. + const DisableSave ds; + + std::vector buf(size); + size_t total = 0; + + while (total < size) { + int ret = read(rfd, buf.data(), buf.size()); + if (ret == -1 && errno == EAGAIN) { + continue; + } + if (ret > 0) { + total += ret; + } + + // Assert to return on first failure. + ASSERT_THAT(ret, SyscallSucceeds()); + } + }); + + std::vector buf(size); + + struct iovec iov = {}; + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + return RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0); +} + +PosixErrorOr PortAvailable(int port, AddressFamily family, SocketType type, + bool reuse_addr) { + if (port < 0) { + return PosixError(EINVAL, "Invalid port"); + } + + // Both Ipv6 and Dualstack are AF_INET6. + int sock_fam = (family == AddressFamily::kIpv4 ? AF_INET : AF_INET6); + int sock_type = (type == SocketType::kTcp ? SOCK_STREAM : SOCK_DGRAM); + ASSIGN_OR_RETURN_ERRNO(auto fd, Socket(sock_fam, sock_type, 0)); + + if (reuse_addr) { + int one = 1; + RETURN_ERROR_IF_SYSCALL_FAIL( + setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))); + } + + // Try to bind. + sockaddr_storage storage = {}; + int storage_size = 0; + if (family == AddressFamily::kIpv4) { + sockaddr_in* addr = reinterpret_cast(&storage); + storage_size = sizeof(*addr); + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + addr->sin_addr.s_addr = htonl(INADDR_ANY); + } else { + sockaddr_in6* addr = reinterpret_cast(&storage); + storage_size = sizeof(*addr); + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(port); + if (family == AddressFamily::kDualStack) { + inet_pton(AF_INET6, "::ffff:0.0.0.0", + reinterpret_cast(&addr->sin6_addr.s6_addr)); + } else { + addr->sin6_addr = in6addr_any; + } + } + + RETURN_ERROR_IF_SYSCALL_FAIL( + bind(fd.get(), reinterpret_cast(&storage), storage_size)); + + // If the user specified 0 as the port, we will return the port that the + // kernel gave us, otherwise we will validate that this socket bound to the + // requested port. + sockaddr_storage bound_storage = {}; + socklen_t bound_storage_size = sizeof(bound_storage); + RETURN_ERROR_IF_SYSCALL_FAIL( + getsockname(fd.get(), reinterpret_cast(&bound_storage), + &bound_storage_size)); + + int available_port = -1; + if (bound_storage.ss_family == AF_INET) { + sockaddr_in* addr = reinterpret_cast(&bound_storage); + available_port = ntohs(addr->sin_port); + } else if (bound_storage.ss_family == AF_INET6) { + sockaddr_in6* addr = reinterpret_cast(&bound_storage); + available_port = ntohs(addr->sin6_port); + } else { + return PosixError(EPROTOTYPE, "Getsockname returned invalid family"); + } + + // If we requested a specific port make sure our bound port is that port. + if (port != 0 && available_port != port) { + return PosixError(EINVAL, + absl::StrCat("Bound port ", available_port, + " was not equal to requested port ", port)); + } + + // If we're trying to do a TCP socket, let's also try to listen. + if (type == SocketType::kTcp) { + RETURN_ERROR_IF_SYSCALL_FAIL(listen(fd.get(), 1)); + } + + return available_port; +} + +PosixError FreeAvailablePort(int port) { + return NoError(); +} + +PosixErrorOr SendMsg(int sock, msghdr* msg, char buf[], int buf_size) { + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + msg->msg_iov = &iov; + msg->msg_iovlen = 1; + + int ret; + RETURN_ERROR_IF_SYSCALL_FAIL(ret = RetryEINTR(sendmsg)(sock, msg, 0)); + return ret; +} + +void RecvNoData(int sock) { + char data = 0; + struct iovec iov; + iov.iov_base = &data; + iov.iov_len = 1; + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h new file mode 100644 index 000000000..e3e741478 --- /dev/null +++ b/test/syscalls/linux/socket_test_util.h @@ -0,0 +1,449 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_SOCKET_TEST_UTIL_H_ +#define GVISOR_TEST_SYSCALLS_SOCKET_TEST_UTIL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/str_format.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// Wrapper for socket(2) that returns a FileDescriptor. +inline PosixErrorOr Socket(int family, int type, int protocol) { + int fd = socket(family, type, protocol); + MaybeSave(); + if (fd < 0) { + return PosixError( + errno, absl::StrFormat("socket(%d, %d, %d)", family, type, protocol)); + } + return FileDescriptor(fd); +} + +// Wrapper for accept(2) that returns a FileDescriptor. +inline PosixErrorOr Accept(int sockfd, sockaddr* addr, + socklen_t* addrlen) { + int fd = RetryEINTR(accept)(sockfd, addr, addrlen); + MaybeSave(); + if (fd < 0) { + return PosixError( + errno, absl::StrFormat("accept(%d, %p, %p)", sockfd, addr, addrlen)); + } + return FileDescriptor(fd); +} + +// Wrapper for accept4(2) that returns a FileDescriptor. +inline PosixErrorOr Accept4(int sockfd, sockaddr* addr, + socklen_t* addrlen, int flags) { + int fd = RetryEINTR(accept4)(sockfd, addr, addrlen, flags); + MaybeSave(); + if (fd < 0) { + return PosixError(errno, absl::StrFormat("accept4(%d, %p, %p, %#x)", sockfd, + addr, addrlen, flags)); + } + return FileDescriptor(fd); +} + +inline ssize_t SendFd(int fd, void* buf, size_t count, int flags) { + return internal::ApplyFileIoSyscall( + [&](size_t completed) { + return sendto(fd, static_cast(buf) + completed, + count - completed, flags, nullptr, 0); + }, + count); +} + +// A Creator is a function that attempts to create and return a new T. (This +// is copy/pasted from cloud/gvisor/api/sandbox_util.h and is just duplicated +// here for clarity.) +template +using Creator = std::function>()>; + +// A SocketPair represents a pair of socket file descriptors owned by the +// SocketPair. +class SocketPair { + public: + virtual ~SocketPair() = default; + + virtual int first_fd() const = 0; + virtual int second_fd() const = 0; + virtual int release_first_fd() = 0; + virtual int release_second_fd() = 0; + virtual const struct sockaddr* first_addr() const = 0; + virtual const struct sockaddr* second_addr() const = 0; + virtual size_t first_addr_size() const = 0; + virtual size_t second_addr_size() const = 0; + virtual size_t first_addr_len() const = 0; + virtual size_t second_addr_len() const = 0; +}; + +// A FDSocketPair is a SocketPair that consists of only a pair of file +// descriptors. +class FDSocketPair : public SocketPair { + public: + FDSocketPair(int first_fd, int second_fd) + : first_(first_fd), second_(second_fd) {} + + int first_fd() const override { return first_.get(); } + int second_fd() const override { return second_.get(); } + int release_first_fd() override { return first_.release(); } + int release_second_fd() override { return second_.release(); } + const struct sockaddr* first_addr() const override { return nullptr; } + const struct sockaddr* second_addr() const override { return nullptr; } + size_t first_addr_size() const override { return 0; } + size_t second_addr_size() const override { return 0; } + size_t first_addr_len() const override { return 0; } + size_t second_addr_len() const override { return 0; } + + private: + FileDescriptor first_; + FileDescriptor second_; +}; + +// CalculateUnixSockAddrLen calculates the length returned by recvfrom(2) and +// recvmsg(2) for Unix sockets. +size_t CalculateUnixSockAddrLen(const char* sun_path); + +// A AddrFDSocketPair is a SocketPair that consists of a pair of file +// descriptors in addition to a pair of socket addresses. +class AddrFDSocketPair : public SocketPair { + public: + AddrFDSocketPair(int first_fd, int second_fd, + const struct sockaddr_un& first_address, + const struct sockaddr_un& second_address) + : first_(first_fd), + second_(second_fd), + first_addr_(to_storage(first_address)), + second_addr_(to_storage(second_address)), + first_len_(CalculateUnixSockAddrLen(first_address.sun_path)), + second_len_(CalculateUnixSockAddrLen(second_address.sun_path)), + first_size_(sizeof(first_address)), + second_size_(sizeof(second_address)) {} + + AddrFDSocketPair(int first_fd, int second_fd, + const struct sockaddr_in& first_address, + const struct sockaddr_in& second_address) + : first_(first_fd), + second_(second_fd), + first_addr_(to_storage(first_address)), + second_addr_(to_storage(second_address)), + first_len_(sizeof(first_address)), + second_len_(sizeof(second_address)), + first_size_(sizeof(first_address)), + second_size_(sizeof(second_address)) {} + + AddrFDSocketPair(int first_fd, int second_fd, + const struct sockaddr_in6& first_address, + const struct sockaddr_in6& second_address) + : first_(first_fd), + second_(second_fd), + first_addr_(to_storage(first_address)), + second_addr_(to_storage(second_address)), + first_len_(sizeof(first_address)), + second_len_(sizeof(second_address)), + first_size_(sizeof(first_address)), + second_size_(sizeof(second_address)) {} + + int first_fd() const override { return first_.get(); } + int second_fd() const override { return second_.get(); } + int release_first_fd() override { return first_.release(); } + int release_second_fd() override { return second_.release(); } + const struct sockaddr* first_addr() const override { + return reinterpret_cast(&first_addr_); + } + const struct sockaddr* second_addr() const override { + return reinterpret_cast(&second_addr_); + } + size_t first_addr_size() const override { return first_size_; } + size_t second_addr_size() const override { return second_size_; } + size_t first_addr_len() const override { return first_len_; } + size_t second_addr_len() const override { return second_len_; } + + private: + // to_storage coverts a sockaddr_* to a sockaddr_storage. + static struct sockaddr_storage to_storage(const sockaddr_un& addr); + static struct sockaddr_storage to_storage(const sockaddr_in& addr); + static struct sockaddr_storage to_storage(const sockaddr_in6& addr); + + FileDescriptor first_; + FileDescriptor second_; + const struct sockaddr_storage first_addr_; + const struct sockaddr_storage second_addr_; + const size_t first_len_; + const size_t second_len_; + const size_t first_size_; + const size_t second_size_; +}; + +// SyscallSocketPairCreator returns a Creator that obtains file +// descriptors by invoking the socketpair() syscall. +Creator SyscallSocketPairCreator(int domain, int type, + int protocol); + +// SyscallSocketCreator returns a Creator that obtains a file +// descriptor by invoking the socket() syscall. +Creator SyscallSocketCreator(int domain, int type, + int protocol); + +// FilesystemBidirectionalBindSocketPairCreator returns a Creator +// that obtains file descriptors by invoking the bind() and connect() syscalls +// on filesystem paths. Only works for DGRAM sockets. +Creator FilesystemBidirectionalBindSocketPairCreator(int domain, + int type, + int protocol); + +// AbstractBidirectionalBindSocketPairCreator returns a Creator that +// obtains file descriptors by invoking the bind() and connect() syscalls on +// abstract namespace paths. Only works for DGRAM sockets. +Creator AbstractBidirectionalBindSocketPairCreator(int domain, + int type, + int protocol); + +// SocketpairGoferSocketPairCreator returns a Creator that +// obtains file descriptors by connect() syscalls on two sockets with socketpair +// gofer paths. +Creator SocketpairGoferSocketPairCreator(int domain, int type, + int protocol); + +// SocketpairGoferFileSocketPairCreator returns a Creator that +// obtains file descriptors by open() syscalls on socketpair gofer paths. +Creator SocketpairGoferFileSocketPairCreator(int flags); + +// FilesystemAcceptBindSocketPairCreator returns a Creator that +// obtains file descriptors by invoking the accept() and bind() syscalls on +// a filesystem path. Only works for STREAM and SEQPACKET sockets. +Creator FilesystemAcceptBindSocketPairCreator(int domain, int type, + int protocol); + +// AbstractAcceptBindSocketPairCreator returns a Creator that +// obtains file descriptors by invoking the accept() and bind() syscalls on a +// abstract namespace path. Only works for STREAM and SEQPACKET sockets. +Creator AbstractAcceptBindSocketPairCreator(int domain, int type, + int protocol); + +// FilesystemUnboundSocketPairCreator returns a Creator that obtains +// file descriptors by invoking the socket() syscall and generates a filesystem +// path for binding. +Creator FilesystemUnboundSocketPairCreator(int domain, int type, + int protocol); + +// AbstractUnboundSocketPairCreator returns a Creator that obtains +// file descriptors by invoking the socket() syscall and generates an abstract +// path for binding. +Creator AbstractUnboundSocketPairCreator(int domain, int type, + int protocol); + +// TCPAcceptBindSocketPairCreator returns a Creator that obtains +// file descriptors by invoking the accept() and bind() syscalls on TCP sockets. +Creator TCPAcceptBindSocketPairCreator(int domain, int type, + int protocol, + bool dual_stack); + +// UDPBidirectionalBindSocketPairCreator returns a Creator that +// obtains file descriptors by invoking the bind() and connect() syscalls on UDP +// sockets. +Creator UDPBidirectionalBindSocketPairCreator(int domain, int type, + int protocol, + bool dual_stack); + +// A SocketPairKind couples a human-readable description of a socket pair with +// a function that creates such a socket pair. +struct SocketPairKind { + std::string description; + Creator creator; + + // Create creates a socket pair of this kind. + PosixErrorOr> Create() const { return creator(); } +}; + +// A SocketKind couples a human-readable description of a socket with +// a function that creates such a socket. +struct SocketKind { + std::string description; + Creator creator; + + // Create creates a socket pair of this kind. + PosixErrorOr> Create() const { + return creator(); + } +}; + +// A ReversedSocketPair wraps another SocketPair but flips the first and second +// file descriptors. ReversedSocketPair is used to test socket pairs that +// should be symmetric. +class ReversedSocketPair : public SocketPair { + public: + explicit ReversedSocketPair(std::unique_ptr base) + : base_(std::move(base)) {} + + int first_fd() const override { return base_->second_fd(); } + int second_fd() const override { return base_->first_fd(); } + int release_first_fd() override { return base_->release_second_fd(); } + int release_second_fd() override { return base_->release_first_fd(); } + const struct sockaddr* first_addr() const override { + return base_->second_addr(); + } + const struct sockaddr* second_addr() const override { + return base_->first_addr(); + } + size_t first_addr_size() const override { return base_->second_addr_size(); } + size_t second_addr_size() const override { return base_->first_addr_size(); } + size_t first_addr_len() const override { return base_->second_addr_len(); } + size_t second_addr_len() const override { return base_->first_addr_len(); } + + private: + std::unique_ptr base_; +}; + +// Reversed returns a SocketPairKind that represents SocketPairs created by +// flipping the file descriptors provided by another SocketPair. +SocketPairKind Reversed(SocketPairKind const& base); + +// IncludeReversals returns a vector that returns all +// SocketPairKinds in `vec` as well as all SocketPairKinds obtained by flipping +// the file descriptors provided by the kinds in `vec`. +std::vector IncludeReversals(std::vector vec); + +// A Middleware is a function wraps a SocketPairKind. +using Middleware = std::function; + +// Reversed returns a SocketPairKind that represents SocketPairs created by +// flipping the file descriptors provided by another SocketPair. +template +Middleware SetSockOpt(int level, int optname, T* value) { + return [=](SocketPairKind const& base) { + auto const& creator = base.creator; + return SocketPairKind{ + absl::StrCat("setsockopt(", level, ", ", optname, ", ", *value, ") ", + base.description), + [creator, level, optname, + value]() -> PosixErrorOr> { + ASSIGN_OR_RETURN_ERRNO(auto creator_value, creator()); + if (creator_value->first_fd() >= 0) { + RETURN_ERROR_IF_SYSCALL_FAIL(setsockopt( + creator_value->first_fd(), level, optname, value, sizeof(T))); + } + if (creator_value->second_fd() >= 0) { + RETURN_ERROR_IF_SYSCALL_FAIL(setsockopt( + creator_value->second_fd(), level, optname, value, sizeof(T))); + } + return creator_value; + }}; + }; +} + +constexpr int kSockOptOn = 1; +constexpr int kSockOptOff = 0; + +// NoOp returns the same SocketPairKind that it is passed. +SocketPairKind NoOp(SocketPairKind const& base); + +// TransferTest tests that data can be send back and fourth between two +// specified FDs. Note that calls to this function should be wrapped in +// ASSERT_NO_FATAL_FAILURE(). +void TransferTest(int fd1, int fd2); + +// Fills [buf, buf+len) with random bytes. +void RandomizeBuffer(char* buf, size_t len); + +// Base test fixture for tests that operate on pairs of connected sockets. +class SocketPairTest : public ::testing::TestWithParam { + protected: + SocketPairTest() { + // gUnit uses printf, so so will we. + printf("Testing with %s\n", GetParam().description.c_str()); + } + + PosixErrorOr> NewSocketPair() const { + return GetParam().Create(); + } +}; + +// Base test fixture for tests that operate on simple Sockets. +class SimpleSocketTest : public ::testing::TestWithParam { + protected: + SimpleSocketTest() { + // gUnit uses printf, so so will we. + printf("Testing with %s\n", GetParam().description.c_str()); + } + + PosixErrorOr> NewSocket() const { + return GetParam().Create(); + } +}; + +SocketKind SimpleSocket(int fam, int type, int proto); + +// Send a buffer of size 'size' to sockets->first_fd(), returning the result of +// sendmsg. +// +// If reader, read from second_fd() until size bytes have been read. +ssize_t SendLargeSendMsg(const std::unique_ptr& sockets, + size_t size, bool reader); + +// Initializes the given buffer with random data. +void RandomizeBuffer(char* ptr, size_t len); + +enum class AddressFamily { kIpv4 = 1, kIpv6 = 2, kDualStack = 3 }; +enum class SocketType { kUdp = 1, kTcp = 2 }; + +// Returns a PosixError or a port that is available. If 0 is specified as the +// port it will bind port 0 (and allow the kernel to select any free port). +// Otherwise, it will try to bind the specified port and validate that it can be +// used for the requested family and socket type. The final option is +// reuse_addr. This specifies whether SO_REUSEADDR should be applied before a +// bind(2) attempt. SO_REUSEADDR means that sockets in TIME_WAIT states or other +// bound UDP sockets would not cause an error on bind(2). This option should be +// set if subsequent calls to bind on the returned port will also use +// SO_REUSEADDR. +// +// Note: That this test will attempt to bind the ANY address for the respective +// protocol. +PosixErrorOr PortAvailable(int port, AddressFamily family, SocketType type, + bool reuse_addr); + +// FreeAvailablePort is used to return a port that was obtained by using +// the PortAvailable helper with port 0. +PosixError FreeAvailablePort(int port); + +// SendMsg converts a buffer to an iovec and adds it to msg before sending it. +PosixErrorOr SendMsg(int sock, msghdr* msg, char buf[], int buf_size); + +// RecvNoData checks that no data is receivable on sock. +void RecvNoData(int sock); + +// Base test fixture for tests that apply to all kinds of pairs of connected +// sockets. +using AllSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_SOCKET_TEST_UTIL_H_ diff --git a/test/syscalls/linux/socket_unix.cc b/test/syscalls/linux/socket_unix.cc new file mode 100644 index 000000000..c60a965ae --- /dev/null +++ b/test/syscalls/linux/socket_unix.cc @@ -0,0 +1,1181 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_unix.h" + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +// This file is a generic socket test file. It must be built with another file +// that provides the test types. + +namespace gvisor { +namespace testing { + +namespace { + +TEST_P(UnixSocketPairTest, BasicFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char received_data[20]; + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, + sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); +} + +TEST_P(UnixSocketPairTest, BasicTwoFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair1 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + auto pair2 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + int sent_fds[] = {pair1->second_fd(), pair2->second_fd()}; + + ASSERT_NO_FATAL_FAILURE( + SendFDs(sockets->first_fd(), sent_fds, 2, sent_data, sizeof(sent_data))); + + char received_data[20]; + int received_fds[] = {-1, -1}; + + ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 2, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd())); + ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd())); +} + +TEST_P(UnixSocketPairTest, BasicThreeFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair1 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + auto pair2 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + auto pair3 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + int sent_fds[] = {pair1->second_fd(), pair2->second_fd(), pair3->second_fd()}; + + ASSERT_NO_FATAL_FAILURE( + SendFDs(sockets->first_fd(), sent_fds, 3, sent_data, sizeof(sent_data))); + + char received_data[20]; + int received_fds[] = {-1, -1, -1}; + + ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 3, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd())); + ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd())); + ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[2], pair3->first_fd())); +} + +TEST_P(UnixSocketPairTest, BadFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + int sent_fd = -1; + + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(sent_fd))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(sent_fd)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &sent_fd, sizeof(sent_fd)); + + struct iovec iov; + iov.iov_base = sent_data; + iov.iov_len = sizeof(sent_data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), + SyscallFailsWithErrno(EBADF)); +} + +// BasicFDPassNoSpace starts off by sending a single FD just like BasicFDPass. +// The difference is that when calling recvmsg, no space for FDs is provided, +// only space for the cmsg header. +TEST_P(UnixSocketPairTest, BasicFDPassNoSpace) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char received_data[20]; + + struct msghdr msg = {}; + std::vector control(CMSG_SPACE(0)); + msg.msg_control = &control[0]; + msg.msg_controllen = control.size(); + + struct iovec iov; + iov.iov_base = received_data; + iov.iov_len = sizeof(received_data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), + SyscallSucceedsWithValue(sizeof(received_data))); + + EXPECT_EQ(msg.msg_controllen, 0); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +// BasicFDPassUnalignedRecv starts off by sending a single FD just like +// BasicFDPass. The difference is that when calling recvmsg, the length of the +// receive data is only aligned on a 4 byte boundry instead of the normal 8. +TEST_P(UnixSocketPairTest, BasicFDPassUnalignedRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char received_data[20]; + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvSingleFDUnaligned( + sockets->second_fd(), &fd, received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); +} + +TEST_P(UnixSocketPairTest, ConcurrentBasicFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + int sockfd1 = sockets->first_fd(); + auto recv_func = [sockfd1, sent_data]() { + char received_data[20]; + int fd = -1; + RecvSingleFD(sockfd1, &fd, received_data, sizeof(received_data)); + ASSERT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + char buf[20]; + ASSERT_THAT(ReadFd(fd, buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + ASSERT_THAT(WriteFd(fd, buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + }; + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->second_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + ScopedThread t(recv_func); + + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(WriteFd(pair->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[20]; + ASSERT_THAT(ReadFd(pair->first_fd(), received_data, sizeof(received_data)), + SyscallSucceedsWithValue(sizeof(received_data))); + + t.Join(); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +// FDPassNoRecv checks that the control message can be safely ignored by using +// read(2) instead of recvmsg(2). +TEST_P(UnixSocketPairTest, FDPassNoRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + // Read while ignoring the passed FD. + char received_data[20]; + ASSERT_THAT( + ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), + SyscallSucceedsWithValue(sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + // Check that the socket still works for reads and writes. + ASSERT_NO_FATAL_FAILURE( + TransferTest(sockets->first_fd(), sockets->second_fd())); +} + +// FDPassInterspersed1 checks that sent control messages cannot be read before +// their associated data has been read. +TEST_P(UnixSocketPairTest, FDPassInterspersed1) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char written_data[20]; + RandomizeBuffer(written_data, sizeof(written_data)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)), + SyscallSucceedsWithValue(sizeof(written_data))); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + // Check that we don't get a control message, but do get the data. + char received_data[20]; + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)); + EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data))); +} + +// FDPassInterspersed2 checks that sent control messages cannot be read after +// their assocated data has been read while ignoring the control message by +// using read(2) instead of recvmsg(2). +TEST_P(UnixSocketPairTest, FDPassInterspersed2) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char written_data[20]; + RandomizeBuffer(written_data, sizeof(written_data)); + ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)), + SyscallSucceedsWithValue(sizeof(written_data))); + + char received_data[20]; + ASSERT_THAT( + ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), + SyscallSucceedsWithValue(sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data))); +} + +TEST_P(UnixSocketPairTest, FDPassNotCoalesced) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + auto pair1 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(), + sent_data1, sizeof(sent_data1))); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + auto pair2 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(), + sent_data2, sizeof(sent_data2))); + + char received_data1[sizeof(sent_data1) + sizeof(sent_data2)]; + int received_fd1 = -1; + + RecvSingleFD(sockets->second_fd(), &received_fd1, received_data1, + sizeof(received_data1), sizeof(sent_data1)); + + EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1))); + TransferTest(pair1->first_fd(), pair1->second_fd()); + + char received_data2[sizeof(sent_data1) + sizeof(sent_data2)]; + int received_fd2 = -1; + + RecvSingleFD(sockets->second_fd(), &received_fd2, received_data2, + sizeof(received_data2), sizeof(sent_data2)); + + EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2))); + TransferTest(pair2->first_fd(), pair2->second_fd()); +} + +TEST_P(UnixSocketPairTest, FDPassPeek) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char peek_data[20]; + int peek_fd = -1; + PeekSingleFD(sockets->second_fd(), &peek_fd, peek_data, sizeof(peek_data)); + EXPECT_EQ(0, memcmp(sent_data, peek_data, sizeof(sent_data))); + TransferTest(peek_fd, pair->first_fd()); + EXPECT_THAT(close(peek_fd), SyscallSucceeds()); + + char received_data[20]; + int received_fd = -1; + RecvSingleFD(sockets->second_fd(), &received_fd, received_data, + sizeof(received_data)); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + TransferTest(received_fd, pair->first_fd()); + EXPECT_THAT(close(received_fd), SyscallSucceeds()); +} + +TEST_P(UnixSocketPairTest, BasicCredPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + struct ucred sent_creds; + + ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); + + ASSERT_NO_FATAL_FAILURE( + SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[20]; + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + EXPECT_EQ(sent_creds.pid, received_creds.pid); + EXPECT_EQ(sent_creds.uid, received_creds.uid); + EXPECT_EQ(sent_creds.gid, received_creds.gid); +} + +TEST_P(UnixSocketPairTest, SendNullCredsBeforeSoPassCredRecvEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[20]; + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds { + 0, 65534, 65534 + }; + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixSocketPairTest, SendNullCredsAfterSoPassCredRecvEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + SetSoPassCred(sockets->second_fd()); + + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); + + char received_data[20]; + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds; + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixSocketPairTest, SendNullCredsBeforeSoPassCredSendEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); + + SetSoPassCred(sockets->first_fd()); + + char received_data[20]; + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(UnixSocketPairTest, SendNullCredsAfterSoPassCredSendEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + SetSoPassCred(sockets->first_fd()); + + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); + + char received_data[20]; + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(UnixSocketPairTest, SendNullCredsBeforeSoPassCredRecvEndAfterSendEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + SetSoPassCred(sockets->first_fd()); + + ASSERT_NO_FATAL_FAILURE( + SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[20]; + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds; + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixSocketPairTest, WriteBeforeSoPassCredRecvEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[20]; + + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds { + 0, 65534, 65534 + }; + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixSocketPairTest, WriteAfterSoPassCredRecvEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + SetSoPassCred(sockets->second_fd()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[20]; + + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds; + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixSocketPairTest, WriteBeforeSoPassCredSendEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + SetSoPassCred(sockets->first_fd()); + + char received_data[20]; + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(UnixSocketPairTest, WriteAfterSoPassCredSendEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + SetSoPassCred(sockets->first_fd()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[20]; + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(UnixSocketPairTest, WriteBeforeSoPassCredRecvEndAfterSendEnd) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + SetSoPassCred(sockets->first_fd()); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[20]; + + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds; + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixSocketPairTest, SoPassCred) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int opt; + socklen_t optLen = sizeof(opt); + EXPECT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), + SyscallSucceeds()); + EXPECT_FALSE(opt); + + optLen = sizeof(opt); + EXPECT_THAT( + getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), + SyscallSucceeds()); + EXPECT_FALSE(opt); + + SetSoPassCred(sockets->first_fd()); + + optLen = sizeof(opt); + EXPECT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), + SyscallSucceeds()); + EXPECT_TRUE(opt); + + optLen = sizeof(opt); + EXPECT_THAT( + getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), + SyscallSucceeds()); + EXPECT_FALSE(opt); + + int zero = 0; + EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &zero, + sizeof(zero)), + SyscallSucceeds()); + + optLen = sizeof(opt); + EXPECT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), + SyscallSucceeds()); + EXPECT_FALSE(opt); + + optLen = sizeof(opt); + EXPECT_THAT( + getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), + SyscallSucceeds()); + EXPECT_FALSE(opt); +} + +TEST_P(UnixSocketPairTest, NoDataCredPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + struct msghdr msg = {}; + + struct iovec iov; + iov.iov_base = sent_data; + iov.iov_len = sizeof(sent_data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char control[CMSG_SPACE(0)]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(0); + + ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(UnixSocketPairTest, NoPassCred) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + struct ucred sent_creds; + + ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); + + ASSERT_NO_FATAL_FAILURE( + SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); + + char received_data[20]; + + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(UnixSocketPairTest, CredAndFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + struct ucred sent_creds; + + ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendCredsAndFD(sockets->first_fd(), sent_creds, + pair->second_fd(), sent_data, + sizeof(sent_data))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[20]; + struct ucred received_creds; + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds, + &fd, received_data, + sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + EXPECT_EQ(sent_creds.pid, received_creds.pid); + EXPECT_EQ(sent_creds.uid, received_creds.uid); + EXPECT_EQ(sent_creds.gid, received_creds.gid); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); +} + +TEST_P(UnixSocketPairTest, FDPassBeforeSoPassCred) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[20]; + struct ucred received_creds; + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds, + &fd, received_data, + sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds { + 0, 65534, 65534 + }; + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); +} + +TEST_P(UnixSocketPairTest, FDPassAfterSoPassCred) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + SetSoPassCred(sockets->second_fd()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char received_data[20]; + struct ucred received_creds; + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds, + &fd, received_data, + sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + struct ucred want_creds; + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); +} + +TEST_P(UnixSocketPairTest, CloexecDroppedWhenFDPassed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = ASSERT_NO_ERRNO_AND_VALUE( + UnixDomainSocketPair(SOCK_SEQPACKET | SOCK_CLOEXEC).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char received_data[20]; + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, + sizeof(received_data))); + + EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(0)); +} + +TEST_P(UnixSocketPairTest, CloexecRecvFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(int))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct iovec iov; + char received_data[20]; + iov.iov_base = received_data; + iov.iov_len = sizeof(received_data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_CMSG_CLOEXEC), + SyscallSucceedsWithValue(sizeof(received_data))); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg, nullptr); + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); + ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); + + int fd = -1; + memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); + + EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); +} + +TEST_P(UnixSocketPairTest, FDPassAfterSoPassCredWithoutCredSpace) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + SetSoPassCred(sockets->second_fd()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + struct msghdr msg = {}; + char control[CMSG_LEN(0)]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + char received_data[20]; + struct iovec iov; + iov.iov_base = received_data; + iov.iov_len = sizeof(received_data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), + SyscallSucceedsWithValue(sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + EXPECT_EQ(msg.msg_controllen, sizeof(control)); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg, nullptr); + EXPECT_EQ(cmsg->cmsg_len, sizeof(control)); + EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); + EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); +} + +// This test will validate that MSG_CTRUNC as an input flag to recvmsg will +// not appear as an output flag on the control message when truncation doesn't +// happen. +TEST_P(UnixSocketPairTest, MsgCtruncInputIsNoop) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(int)) /* we're passing a single fd */]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct iovec iov; + char received_data[20]; + iov.iov_base = received_data; + iov.iov_len = sizeof(received_data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_CTRUNC), + SyscallSucceedsWithValue(sizeof(received_data))); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg, nullptr); + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); + ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); + + // Now we should verify that MSG_CTRUNC wasn't set as an output flag. + EXPECT_EQ(msg.msg_flags & MSG_CTRUNC, 0); +} + +TEST_P(UnixSocketPairTest, FDPassAfterSoPassCredWithoutCredHeaderSpace) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + SetSoPassCred(sockets->second_fd()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + struct msghdr msg = {}; + char control[CMSG_LEN(0) / 2]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + char received_data[20]; + struct iovec iov; + iov.iov_base = received_data; + iov.iov_len = sizeof(received_data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), + SyscallSucceedsWithValue(sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + EXPECT_EQ(msg.msg_controllen, 0); +} + +TEST_P(UnixSocketPairTest, InvalidGetSockOpt) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + int opt; + socklen_t optlen = sizeof(opt); + EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, -1, &opt, &optlen), + SyscallFailsWithErrno(ENOPROTOOPT)); +} + +TEST_P(UnixSocketPairTest, BindToBadName) { + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + constexpr char kBadName[] = "/some/path/that/does/not/exist"; + sockaddr_un sockaddr; + sockaddr.sun_family = AF_LOCAL; + memcpy(sockaddr.sun_path, kBadName, sizeof(kBadName)); + + EXPECT_THAT( + bind(pair->first_fd(), reinterpret_cast(&sockaddr), + sizeof(sockaddr)), + SyscallFailsWithErrno(ENOENT)); +} + +TEST_P(UnixSocketPairTest, RecvmmsgTimeoutAfterRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[10]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + char received_data[sizeof(sent_data) * 2]; + std::vector msgs(2); + std::vector iovs(msgs.size()); + const int chunk_size = sizeof(received_data) / msgs.size(); + for (size_t i = 0; i < msgs.size(); i++) { + iovs[i].iov_len = chunk_size; + iovs[i].iov_base = &received_data[i * chunk_size]; + msgs[i].msg_hdr.msg_iov = &iovs[i]; + msgs[i].msg_hdr.msg_iovlen = 1; + } + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), + SyscallSucceedsWithValue(sizeof(sent_data))); + + struct timespec timeout = {0, 1}; + ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->second_fd(), &msgs[0], msgs.size(), + 0, &timeout), + SyscallSucceedsWithValue(1)); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + EXPECT_EQ(chunk_size, msgs[0].msg_len); +} + +TEST_P(UnixSocketPairTest, TIOCINQSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int size = -1; + EXPECT_THAT(ioctl(sockets->first_fd(), TIOCINQ, &size), SyscallSucceeds()); + EXPECT_EQ(size, 0); + + const char some_data[] = "dangerzone"; + ASSERT_THAT( + RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), + SyscallSucceeds()); + EXPECT_THAT(ioctl(sockets->first_fd(), TIOCINQ, &size), SyscallSucceeds()); + EXPECT_EQ(size, sizeof(some_data)); + + // Linux only reports the first message's size, which is wrong. We test for + // the behavior described in the man page. + SKIP_IF(!IsRunningOnGvisor()); + + ASSERT_THAT( + RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), + SyscallSucceeds()); + EXPECT_THAT(ioctl(sockets->first_fd(), TIOCINQ, &size), SyscallSucceeds()); + EXPECT_EQ(size, sizeof(some_data) * 2); +} + +TEST_P(UnixSocketPairTest, TIOCOUTQSucceeds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int size = -1; + EXPECT_THAT(ioctl(sockets->second_fd(), TIOCOUTQ, &size), SyscallSucceeds()); + EXPECT_EQ(size, 0); + + // Linux reports bogus numbers which are related to its internal allocations. + // We test for the behavior described in the man page. + SKIP_IF(!IsRunningOnGvisor()); + + const char some_data[] = "dangerzone"; + ASSERT_THAT( + RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), + SyscallSucceeds()); + EXPECT_THAT(ioctl(sockets->second_fd(), TIOCOUTQ, &size), SyscallSucceeds()); + EXPECT_EQ(size, sizeof(some_data)); + + ASSERT_THAT( + RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), + SyscallSucceeds()); + EXPECT_THAT(ioctl(sockets->second_fd(), TIOCOUTQ, &size), SyscallSucceeds()); + EXPECT_EQ(size, sizeof(some_data) * 2); +} + +TEST_P(UnixSocketPairTest, NetdeviceIoctlsSucceed) { + FileDescriptor sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_DGRAM, 0)); + + // Prepare the request. + struct ifreq ifr; + snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); + + // Check that the ioctl either succeeds or fails with ENODEV. + int err = ioctl(sock.get(), SIOCGIFINDEX, &ifr); + if (err < 0) { + ASSERT_EQ(errno, ENODEV); + } +} + +TEST_P(UnixSocketPairTest, SocketShutdown) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char buf[20]; + const std::string data = "abc"; + ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), 3), + SyscallSucceedsWithValue(3)); + ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds()); + ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds()); + + // Shutting down a socket does not clear the buffer. + ASSERT_THAT(ReadFd(sockets->second_fd(), buf, 3), + SyscallSucceedsWithValue(3)); + EXPECT_EQ(data, absl::string_view(buf, 3)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix.h b/test/syscalls/linux/socket_unix.h new file mode 100644 index 000000000..d2a16afb2 --- /dev/null +++ b/test/syscalls/linux/socket_unix.h @@ -0,0 +1,29 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected unix sockets. +using UnixSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_ diff --git a/test/syscalls/linux/socket_unix_abstract.cc b/test/syscalls/linux/socket_unix_abstract.cc new file mode 100644 index 000000000..0878f63ff --- /dev/null +++ b/test/syscalls/linux/socket_unix_abstract.cc @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_unix.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_abstract_nonblock.cc b/test/syscalls/linux/socket_unix_abstract_nonblock.cc new file mode 100644 index 000000000..93fb33832 --- /dev/null +++ b/test/syscalls/linux/socket_unix_abstract_nonblock.cc @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_non_blocking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonBlockingSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_dgram.cc b/test/syscalls/linux/socket_unix_dgram.cc new file mode 100644 index 000000000..c17d3990f --- /dev/null +++ b/test/syscalls/linux/socket_unix_dgram.cc @@ -0,0 +1,45 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_unix_dgram.h" + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST_P(DgramUnixSocketPairTest, WriteOneSideClosed) { + // FIXME: gVisor datagram sockets return EPIPE instead of + // ECONNREFUSED. + SKIP_IF(IsRunningOnGvisor()); + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + constexpr char kStr[] = "abc"; + ASSERT_THAT(write(sockets->second_fd(), kStr, 3), + SyscallFailsWithErrno(ECONNREFUSED)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_dgram.h b/test/syscalls/linux/socket_unix_dgram.h new file mode 100644 index 000000000..722a3d8e6 --- /dev/null +++ b/test/syscalls/linux/socket_unix_dgram.h @@ -0,0 +1,29 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected dgram unix sockets. +using DgramUnixSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_ diff --git a/test/syscalls/linux/socket_unix_dgram_local.cc b/test/syscalls/linux/socket_unix_dgram_local.cc new file mode 100644 index 000000000..b2fa72b5e --- /dev/null +++ b/test/syscalls/linux/socket_unix_dgram_local.cc @@ -0,0 +1,59 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_non_stream.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_unix_dgram.h" +#include "test/syscalls/linux/socket_unix_non_stream.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, DgramUnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixNonStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc new file mode 100644 index 000000000..9152c229c --- /dev/null +++ b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc @@ -0,0 +1,68 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of connected non-blocking dgram +// unix sockets. +using NonBlockingDgramUnixSocketPairTest = SocketPairTest; + +TEST_P(NonBlockingDgramUnixSocketPairTest, ReadOneSideClosed) { + if (IsRunningOnGvisor()) { + // FIXME: gVisor datagram sockets return 0 instead of + // EAGAIN. + return; + } + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + char data[10] = {}; + ASSERT_THAT(read(sockets->second_fd(), data, sizeof(data)), + SyscallFailsWithErrno(EAGAIN)); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonBlockingDgramUnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_domain.cc b/test/syscalls/linux/socket_unix_domain.cc new file mode 100644 index 000000000..f8f0d01eb --- /dev/null +++ b/test/syscalls/linux/socket_unix_domain.cc @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_generic.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, AllSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_filesystem.cc b/test/syscalls/linux/socket_unix_filesystem.cc new file mode 100644 index 000000000..be873edcb --- /dev/null +++ b/test/syscalls/linux/socket_unix_filesystem.cc @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_unix.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc new file mode 100644 index 000000000..63e85ac11 --- /dev/null +++ b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_non_blocking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonBlockingSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_non_stream.cc b/test/syscalls/linux/socket_unix_non_stream.cc new file mode 100644 index 000000000..620397746 --- /dev/null +++ b/test/syscalls/linux/socket_unix_non_stream.cc @@ -0,0 +1,229 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_unix_non_stream.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/memory_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +TEST_P(UnixNonStreamSocketPairTest, RecvMsgTooLarge) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int rcvbuf; + socklen_t length = sizeof(rcvbuf); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &rcvbuf, &length), + SyscallSucceeds()); + + // Make the call larger than the receive buffer. + const int recv_size = 3 * rcvbuf; + + // Write a message that does fit in the receive buffer. + const int write_size = rcvbuf - kPageSize; + + std::vector write_buf(write_size, 'a'); + const int ret = RetryEINTR(write)(sockets->second_fd(), write_buf.data(), + write_buf.size()); + if (ret < 0 && errno == ENOBUFS) { + // NOTE: Linux may stall the write for a long time and + // ultimately return ENOBUFS. Allow this error, since a retry will likely + // result in the same error. + return; + } + ASSERT_THAT(ret, SyscallSucceeds()); + + std::vector recv_buf(recv_size); + + ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->first_fd(), recv_buf.data(), + recv_buf.size(), write_size)); + + recv_buf.resize(write_size); + EXPECT_EQ(recv_buf, write_buf); +} + +// Create a region of anonymous memory of size 'size', which is fragmented in +// FileMem. +// +// ptr contains the start address of the region. The returned vector contains +// all of the mappings to be unmapped when done. +PosixErrorOr> CreateFragmentedRegion(const int size, + void** ptr) { + Mapping region; + ASSIGN_OR_RETURN_ERRNO(region, Mmap(nullptr, size, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + + *ptr = region.ptr(); + + // Don't save hundreds of times for all of these mmaps. + DisableSave ds; + + std::vector pages; + + // Map and commit a single page at a time, mapping and committing an unrelated + // page between each call to force FileMem fragmentation. + for (uintptr_t addr = region.addr(); addr < region.endaddr(); + addr += kPageSize) { + Mapping page; + ASSIGN_OR_RETURN_ERRNO( + page, + Mmap(reinterpret_cast(addr), kPageSize, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0)); + *reinterpret_cast(page.ptr()) = 42; + + pages.emplace_back(std::move(page)); + + // Unrelated page elsewhere. + ASSIGN_OR_RETURN_ERRNO(page, + Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + *reinterpret_cast(page.ptr()) = 42; + + pages.emplace_back(std::move(page)); + } + + // The mappings above have taken ownership of the region. + region.release(); + + return pages; +} + +// A contiguous iov that is heavily fragmented in FileMem can still be sent +// successfully. +TEST_P(UnixNonStreamSocketPairTest, FragmentedSendMsg) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + const int buffer_size = UIO_MAXIOV * kPageSize; + // Extra page for message header overhead. + const int sndbuf = buffer_size + kPageSize; + // N.B. setsockopt(SO_SNDBUF) doubles the passed value. + const int set_sndbuf = sndbuf / 2; + + EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, + &set_sndbuf, sizeof(set_sndbuf)), + SyscallSucceeds()); + + int actual_sndbuf = 0; + socklen_t length = sizeof(actual_sndbuf); + ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, + &actual_sndbuf, &length), + SyscallSucceeds()); + + if (actual_sndbuf != sndbuf) { + // Unable to get the sndbuf we want. + // + // N.B. At minimum, the socketpair gofer should provide a socket that is + // already the correct size. + // + // TODO: When internal UDS support SO_SNDBUF, we can assert that + // we always get the right SO_SNDBUF on gVisor. + LOG(INFO) << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf + << ". Skipping test"; + return; + } + + // Create a contiguous region of memory of 2*UIO_MAXIOV*PAGE_SIZE. We'll call + // sendmsg with a single iov, but the goal is to get the sentry to split this + // into > UIO_MAXIOV iovs when calling the kernel. + void* ptr; + std::vector pages = + ASSERT_NO_ERRNO_AND_VALUE(CreateFragmentedRegion(buffer_size, &ptr)); + + struct iovec iov = {}; + iov.iov_base = ptr; + iov.iov_len = buffer_size; + + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // NOTE: Linux has poor behavior in the presence of + // physical memory fragmentation. As a result, this may stall for a long time + // and ultimately return ENOBUFS. Allow this error, since it means that we + // made it to the host kernel and started the sendmsg. + EXPECT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), + AnyOf(SyscallSucceedsWithValue(buffer_size), + SyscallFailsWithErrno(ENOBUFS))); +} + +// A contiguous iov that is heavily fragmented in FileMem can still be received +// into successfully. +TEST_P(UnixNonStreamSocketPairTest, FragmentedRecvMsg) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + const int buffer_size = UIO_MAXIOV * kPageSize; + // Extra page for message header overhead. + const int sndbuf = buffer_size + kPageSize; + // N.B. setsockopt(SO_SNDBUF) doubles the passed value. + const int set_sndbuf = sndbuf / 2; + + EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, + &set_sndbuf, sizeof(set_sndbuf)), + SyscallSucceeds()); + + int actual_sndbuf = 0; + socklen_t length = sizeof(actual_sndbuf); + ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, + &actual_sndbuf, &length), + SyscallSucceeds()); + + if (actual_sndbuf != sndbuf) { + // Unable to get the sndbuf we want. + // + // N.B. At minimum, the socketpair gofer should provide a socket that is + // already the correct size. + // + // TODO: When internal UDS support SO_SNDBUF, we can assert that + // we always get the right SO_SNDBUF on gVisor. + LOG(INFO) << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf + << ". Skipping test"; + return; + } + + std::vector write_buf(buffer_size, 'a'); + const int ret = RetryEINTR(write)(sockets->first_fd(), write_buf.data(), + write_buf.size()); + if (ret < 0 && errno == ENOBUFS) { + // NOTE: Linux may stall the write for a long time and + // ultimately return ENOBUFS. Allow this error, since a retry will likely + // result in the same error. + return; + } + ASSERT_THAT(ret, SyscallSucceeds()); + + // Create a contiguous region of memory of 2*UIO_MAXIOV*PAGE_SIZE. We'll call + // sendmsg with a single iov, but the goal is to get the sentry to split this + // into > UIO_MAXIOV iovs when calling the kernel. + void* ptr; + std::vector pages = + ASSERT_NO_ERRNO_AND_VALUE(CreateFragmentedRegion(buffer_size, &ptr)); + + ASSERT_NO_FATAL_FAILURE(RecvNoCmsg( + sockets->second_fd(), reinterpret_cast(ptr), buffer_size)); + + EXPECT_EQ(0, memcmp(write_buf.data(), ptr, buffer_size)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_non_stream.h b/test/syscalls/linux/socket_unix_non_stream.h new file mode 100644 index 000000000..e4214d949 --- /dev/null +++ b/test/syscalls/linux/socket_unix_non_stream.h @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected non-stream +// unix-domain sockets. +using UnixNonStreamSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_ diff --git a/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc new file mode 100644 index 000000000..c5d525dde --- /dev/null +++ b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc @@ -0,0 +1,47 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_non_stream_blocking.h" + +#include + +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, BlockingNonStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_pair.cc b/test/syscalls/linux/socket_unix_pair.cc new file mode 100644 index 000000000..85dd3711b --- /dev/null +++ b/test/syscalls/linux/socket_unix_pair.cc @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_unix.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_pair_nonblock.cc b/test/syscalls/linux/socket_unix_pair_nonblock.cc new file mode 100644 index 000000000..6a40fe68c --- /dev/null +++ b/test/syscalls/linux/socket_unix_pair_nonblock.cc @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_non_blocking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonBlockingSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_seqpacket.cc b/test/syscalls/linux/socket_unix_seqpacket.cc new file mode 100644 index 000000000..ad0af77e9 --- /dev/null +++ b/test/syscalls/linux/socket_unix_seqpacket.cc @@ -0,0 +1,49 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_unix_seqpacket.h" + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST_P(SeqpacketUnixSocketPairTest, WriteOneSideClosed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + constexpr char kStr[] = "abc"; + ASSERT_THAT(write(sockets->second_fd(), kStr, 3), + SyscallFailsWithErrno(EPIPE)); +} + +TEST_P(SeqpacketUnixSocketPairTest, ReadOneSideClosed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + char data[10] = {}; + ASSERT_THAT(read(sockets->second_fd(), data, sizeof(data)), + SyscallSucceedsWithValue(0)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_seqpacket.h b/test/syscalls/linux/socket_unix_seqpacket.h new file mode 100644 index 000000000..da8eb2b2b --- /dev/null +++ b/test/syscalls/linux/socket_unix_seqpacket.h @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected seqpacket unix +// sockets. +using SeqpacketUnixSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_ diff --git a/test/syscalls/linux/socket_unix_seqpacket_local.cc b/test/syscalls/linux/socket_unix_seqpacket_local.cc new file mode 100644 index 000000000..f9139a754 --- /dev/null +++ b/test/syscalls/linux/socket_unix_seqpacket_local.cc @@ -0,0 +1,59 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_non_stream.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_unix_non_stream.h" +#include "test/syscalls/linux/socket_unix_seqpacket.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat(VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, SeqpacketUnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixNonStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream.cc b/test/syscalls/linux/socket_unix_stream.cc new file mode 100644 index 000000000..8232c9e35 --- /dev/null +++ b/test/syscalls/linux/socket_unix_stream.cc @@ -0,0 +1,69 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of connected stream unix sockets. +using StreamUnixSocketPairTest = SocketPairTest; + +TEST_P(StreamUnixSocketPairTest, WriteOneSideClosed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + constexpr char kStr[] = "abc"; + ASSERT_THAT(write(sockets->second_fd(), kStr, 3), + SyscallFailsWithErrno(EPIPE)); +} + +TEST_P(StreamUnixSocketPairTest, ReadOneSideClosed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); + char data[10] = {}; + ASSERT_THAT(read(sockets->second_fd(), data, sizeof(data)), + SyscallSucceedsWithValue(0)); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, StreamUnixSocketPairTest, + ::testing::ValuesIn(IncludeReversals(VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream_blocking_local.cc b/test/syscalls/linux/socket_unix_stream_blocking_local.cc new file mode 100644 index 000000000..1cdeadd27 --- /dev/null +++ b/test/syscalls/linux/socket_unix_stream_blocking_local.cc @@ -0,0 +1,47 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/socket_stream_blocking.h" + +#include + +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, BlockingStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream_local.cc b/test/syscalls/linux/socket_unix_stream_local.cc new file mode 100644 index 000000000..9f11e2d49 --- /dev/null +++ b/test/syscalls/linux/socket_unix_stream_local.cc @@ -0,0 +1,49 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include + +#include "test/syscalls/linux/socket_stream.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, StreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream_nonblock_local.cc b/test/syscalls/linux/socket_unix_stream_nonblock_local.cc new file mode 100644 index 000000000..4c3d3a187 --- /dev/null +++ b/test/syscalls/linux/socket_unix_stream_nonblock_local.cc @@ -0,0 +1,49 @@ +// Copyright 2018 Google LLC +// +// 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. +#include "test/syscalls/linux/socket_stream_nonblock.h" + +#include + +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSocketPairs() { + return VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, NonBlockingStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_abstract.cc b/test/syscalls/linux/socket_unix_unbound_abstract.cc new file mode 100644 index 000000000..a35b3b9bd --- /dev/null +++ b/test/syscalls/linux/socket_unix_unbound_abstract.cc @@ -0,0 +1,116 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of unbound abstract unix sockets. +using UnboundAbstractUnixSocketPairTest = SocketPairTest; + +TEST_P(UnboundAbstractUnixSocketPairTest, AddressAfterNull) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct sockaddr_un addr = + *reinterpret_cast(sockets->first_addr()); + ASSERT_EQ(addr.sun_path[sizeof(addr.sun_path) - 1], 0); + SKIP_IF(addr.sun_path[sizeof(addr.sun_path) - 2] != 0 || + addr.sun_path[sizeof(addr.sun_path) - 3] != 0); + + addr.sun_path[sizeof(addr.sun_path) - 2] = 'a'; + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->second_fd(), + reinterpret_cast(&addr), sizeof(addr)), + SyscallSucceeds()); +} + +TEST_P(UnboundAbstractUnixSocketPairTest, ShortAddressNotExtended) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct sockaddr_un addr = + *reinterpret_cast(sockets->first_addr()); + ASSERT_EQ(addr.sun_path[sizeof(addr.sun_path) - 1], 0); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size() - 1), + SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); +} + +TEST_P(UnboundAbstractUnixSocketPairTest, BindNothing) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + ASSERT_THAT(bind(sockets->first_fd(), + reinterpret_cast(&addr), sizeof(addr)), + SyscallSucceeds()); +} + +TEST_P(UnboundAbstractUnixSocketPairTest, GetSockNameFullLength) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + sockaddr_storage addr = {}; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT(getsockname(sockets->first_fd(), + reinterpret_cast(&addr), &addr_len), + SyscallSucceeds()); + EXPECT_EQ(addr_len, sockets->first_addr_size()); +} + +TEST_P(UnboundAbstractUnixSocketPairTest, GetSockNamePartialLength) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size() - 1), + SyscallSucceeds()); + + sockaddr_storage addr = {}; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT(getsockname(sockets->first_fd(), + reinterpret_cast(&addr), &addr_len), + SyscallSucceeds()); + EXPECT_EQ(addr_len, sockets->first_addr_size() - 1); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnboundAbstractUnixSocketPairTest, + ::testing::ValuesIn(ApplyVec( + AbstractUnboundUnixDomainSocketPair, + AllBitwiseCombinations( + List{SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM}, + List{0, SOCK_NONBLOCK}, List{0, SOCK_CLOEXEC})))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_dgram.cc b/test/syscalls/linux/socket_unix_unbound_dgram.cc new file mode 100644 index 000000000..a01b7f644 --- /dev/null +++ b/test/syscalls/linux/socket_unix_unbound_dgram.cc @@ -0,0 +1,162 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of unbound dgram unix sockets. +using UnboundDgramUnixSocketPairTest = SocketPairTest; + +TEST_P(UnboundDgramUnixSocketPairTest, BindConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); +} + +TEST_P(UnboundDgramUnixSocketPairTest, SelfConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(connect(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); +} + +TEST_P(UnboundDgramUnixSocketPairTest, DoubleConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); +} + +TEST_P(UnboundDgramUnixSocketPairTest, GetRemoteAddress) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + socklen_t addressLength = sockets->first_addr_size(); + struct sockaddr_storage address = {}; + ASSERT_THAT(getpeername(sockets->second_fd(), (struct sockaddr*)(&address), + &addressLength), + SyscallSucceeds()); + EXPECT_EQ( + 0, memcmp(&address, sockets->first_addr(), sockets->first_addr_size())); +} + +TEST_P(UnboundDgramUnixSocketPairTest, Sendto) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(sendto(sockets->second_fd(), sent_data, sizeof(sent_data), 0, + sockets->first_addr(), sockets->first_addr_size()), + SyscallSucceedsWithValue(sizeof(sent_data))); + + char received_data[sizeof(sent_data)]; + ASSERT_THAT(ReadFd(sockets->first_fd(), received_data, sizeof(received_data)), + SyscallSucceedsWithValue(sizeof(received_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); +} + +TEST_P(UnboundDgramUnixSocketPairTest, ZeroWriteAllowed) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + char sent_data[3]; + // Send a zero length packet. + ASSERT_THAT(write(sockets->second_fd(), sent_data, 0), + SyscallSucceedsWithValue(0)); + // Receive the packet. + char received_data[sizeof(sent_data)]; + ASSERT_THAT(read(sockets->first_fd(), received_data, sizeof(received_data)), + SyscallSucceedsWithValue(0)); +} + +TEST_P(UnboundDgramUnixSocketPairTest, Listen) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(listen(sockets->first_fd(), 0), SyscallFailsWithErrno(ENOTSUP)); +} + +TEST_P(UnboundDgramUnixSocketPairTest, Accept) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + ASSERT_THAT(accept(sockets->first_fd(), nullptr, nullptr), + SyscallFailsWithErrno(ENOTSUP)); +} + +TEST_P(UnboundDgramUnixSocketPairTest, SendtoWithoutConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + char data = 'a'; + ASSERT_THAT( + RetryEINTR(sendto)(sockets->second_fd(), &data, sizeof(data), 0, + sockets->first_addr(), sockets->first_addr_size()), + SyscallSucceedsWithValue(sizeof(data))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnboundDgramUnixSocketPairTest, + ::testing::ValuesIn(VecCat( + ApplyVec( + FilesystemUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_DGRAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC}))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_filesystem.cc b/test/syscalls/linux/socket_unix_unbound_filesystem.cc new file mode 100644 index 000000000..56d882643 --- /dev/null +++ b/test/syscalls/linux/socket_unix_unbound_filesystem.cc @@ -0,0 +1,84 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of unbound filesystem unix +// sockets. +using UnboundFilesystemUnixSocketPairTest = SocketPairTest; + +TEST_P(UnboundFilesystemUnixSocketPairTest, AddressAfterNull) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct sockaddr_un addr = + *reinterpret_cast(sockets->first_addr()); + ASSERT_EQ(addr.sun_path[sizeof(addr.sun_path) - 1], 0); + SKIP_IF(addr.sun_path[sizeof(addr.sun_path) - 2] != 0 || + addr.sun_path[sizeof(addr.sun_path) - 3] != 0); + + addr.sun_path[sizeof(addr.sun_path) - 2] = 'a'; + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + ASSERT_THAT(bind(sockets->second_fd(), + reinterpret_cast(&addr), sizeof(addr)), + SyscallFailsWithErrno(EADDRINUSE)); +} + +TEST_P(UnboundFilesystemUnixSocketPairTest, GetSockNameLength) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + sockaddr_storage got_addr = {}; + socklen_t got_addr_len = sizeof(got_addr); + ASSERT_THAT( + getsockname(sockets->first_fd(), + reinterpret_cast(&got_addr), &got_addr_len), + SyscallSucceeds()); + + sockaddr_un want_addr = + *reinterpret_cast(sockets->first_addr()); + + EXPECT_EQ(got_addr_len, + strlen(want_addr.sun_path) + 1 + sizeof(want_addr.sun_family)); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnboundFilesystemUnixSocketPairTest, + ::testing::ValuesIn(ApplyVec( + FilesystemUnboundUnixDomainSocketPair, + AllBitwiseCombinations( + List{SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM}, + List{0, SOCK_NONBLOCK}, List{0, SOCK_CLOEXEC})))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc new file mode 100644 index 000000000..fa3b99490 --- /dev/null +++ b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc @@ -0,0 +1,91 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of unbound seqpacket unix sockets. +using UnboundUnixSeqpacketSocketPairTest = SocketPairTest; + +TEST_P(UnboundUnixSeqpacketSocketPairTest, SendtoWithoutConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + char data = 'a'; + ASSERT_THAT(sendto(sockets->second_fd(), &data, sizeof(data), 0, + sockets->first_addr(), sockets->first_addr_size()), + SyscallFailsWithErrno(ENOTCONN)); +} + +TEST_P(UnboundUnixSeqpacketSocketPairTest, SendtoWithoutConnectIgnoresAddr) { + // FIXME: gVisor tries to find /foo/bar and thus returns ENOENT. + if (IsRunningOnGvisor()) { + return; + } + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + // Even a bogus address is completely ignored. + constexpr char kPath[] = "/foo/bar"; + + // Sanity check that kPath doesn't exist. + struct stat s; + ASSERT_THAT(stat(kPath, &s), SyscallFailsWithErrno(ENOENT)); + + struct sockaddr_un addr = {}; + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, kPath, sizeof(kPath)); + + char data = 'a'; + ASSERT_THAT( + sendto(sockets->second_fd(), &data, sizeof(data), 0, + reinterpret_cast(&addr), sizeof(addr)), + SyscallFailsWithErrno(ENOTCONN)); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnboundUnixSeqpacketSocketPairTest, + ::testing::ValuesIn(IncludeReversals(VecCat( + ApplyVec( + FilesystemUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_SEQPACKET}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_stream.cc b/test/syscalls/linux/socket_unix_unbound_stream.cc new file mode 100644 index 000000000..99636b221 --- /dev/null +++ b/test/syscalls/linux/socket_unix_unbound_stream.cc @@ -0,0 +1,738 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/unix_domain_socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Test fixture for tests that apply to pairs of connected unix stream sockets. +using UnixStreamSocketPairTest = SocketPairTest; + +// FDPassPartialRead checks that sent control messages cannot be read after +// any of their assocated data has been read while ignoring the control message +// by using read(2) instead of recvmsg(2). +TEST_P(UnixStreamSocketPairTest, FDPassPartialRead) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data))); + + char received_data[sizeof(sent_data) / 2]; + ASSERT_THAT( + ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), + SyscallSucceedsWithValue(sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); + + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)); + EXPECT_EQ(0, memcmp(sent_data + sizeof(received_data), received_data, + sizeof(received_data))); +} + +TEST_P(UnixStreamSocketPairTest, FDPassCoalescedRead) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + auto pair1 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(), + sent_data1, sizeof(sent_data1))); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + auto pair2 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(), + sent_data2, sizeof(sent_data2))); + + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + ASSERT_THAT( + ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), + SyscallSucceedsWithValue(sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), + sizeof(sent_data2))); +} + +// ZeroLengthMessageFDDiscarded checks that control messages associated with +// zero length messages are discarded. +TEST_P(UnixStreamSocketPairTest, ZeroLengthMessageFDDiscarded) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Zero length arrays are invalid in ISO C++, so allocate one of size 1 and + // send a length of 0. + char sent_data1[1] = {}; + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE( + SendSingleFD(sockets->first_fd(), pair->second_fd(), sent_data1, 0)); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + char received_data[sizeof(sent_data2)] = {}; + + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)); + EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(received_data))); +} + +// FDPassCoalescedRecv checks that control messages not in the first message are +// preserved in a coalesced recv. +TEST_P(UnixStreamSocketPairTest, FDPassCoalescedRecv) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data) / 2), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data + sizeof(sent_data) / 2, + sizeof(sent_data) / 2)); + + char received_data[sizeof(sent_data)]; + + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, + sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); +} + +// ReadsNotCoalescedAfterFDPass checks that messages after a message containing +// an FD control message are not coalesced. +TEST_P(UnixStreamSocketPairTest, ReadsNotCoalescedAfterFDPass) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), + sent_data, sizeof(sent_data) / 2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data + sizeof(sent_data) / 2, + sizeof(sent_data) / 2), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + + char received_data[sizeof(sent_data)]; + + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, + sizeof(received_data), + sizeof(sent_data) / 2)); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(sent_data) / 2)); + + EXPECT_EQ(0, memcmp(sent_data + sizeof(sent_data) / 2, received_data, + sizeof(sent_data) / 2)); +} + +// FDPassNotCombined checks that FD control messages are not combined in a +// coalesced read. +TEST_P(UnixStreamSocketPairTest, FDPassNotCombined) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + auto pair1 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(), + sent_data, sizeof(sent_data) / 2)); + + auto pair2 = + ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); + + ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(), + sent_data + sizeof(sent_data) / 2, + sizeof(sent_data) / 2)); + + char received_data[sizeof(sent_data)]; + + int fd = -1; + ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, + sizeof(received_data), + sizeof(sent_data) / 2)); + + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair1->first_fd())); + + EXPECT_THAT(close(fd), SyscallSucceeds()); + fd = -1; + + ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, + sizeof(received_data), + sizeof(sent_data) / 2)); + + EXPECT_EQ(0, memcmp(sent_data + sizeof(sent_data) / 2, received_data, + sizeof(sent_data) / 2)); + + ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair2->first_fd())); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST_P(UnixStreamSocketPairTest, CredPassPartialRead) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data[20]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + + struct ucred sent_creds; + + ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); + + ASSERT_NO_FATAL_FAILURE( + SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); + + int one = 1; + ASSERT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &one, + sizeof(one)), + SyscallSucceeds()); + + for (int i = 0; i < 2; i++) { + char received_data[10]; + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data), + sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data + i * sizeof(received_data), received_data, + sizeof(received_data))); + EXPECT_EQ(sent_creds.pid, received_creds.pid); + EXPECT_EQ(sent_creds.uid, received_creds.uid); + EXPECT_EQ(sent_creds.gid, received_creds.gid); + } +} + +// Unix stream sockets peek in the same way as datagram sockets. +// +// SinglePeek checks that only a single message is peekable in a single recv. +TEST_P(UnixStreamSocketPairTest, SinglePeek) { + if (!IsRunningOnGvisor()) { + // Don't run this test on linux kernels newer than 4.3.x Linux kernel commit + // 9f389e35674f5b086edd70ed524ca0f287259725 which changes this behavior. We + // used to target 3.11 compatibility, so disable this test on newer kernels. + // + // NOTE: Bring this up to Linux 4.4 compatibility. + auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); + SKIP_IF(version.major > 4 || (version.major == 4 && version.minor >= 3)); + } + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + char sent_data[40]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), sent_data, + sizeof(sent_data) / 2, 0), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + ASSERT_THAT( + RetryEINTR(send)(sockets->first_fd(), sent_data + sizeof(sent_data) / 2, + sizeof(sent_data) / 2, 0), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + char received_data[sizeof(sent_data)]; + for (int i = 0; i < 3; i++) { + memset(received_data, 0, sizeof(received_data)); + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(received_data), MSG_PEEK), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); + } + memset(received_data, 0, sizeof(received_data)); + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(sent_data) / 2, 0), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); + memset(received_data, 0, sizeof(received_data)); + ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, + sizeof(sent_data) / 2, 0), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + EXPECT_EQ(0, memcmp(sent_data + sizeof(sent_data) / 2, received_data, + sizeof(sent_data) / 2)); +} + +TEST_P(UnixStreamSocketPairTest, CredsNotCoalescedUp) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + SetSoPassCred(sockets->second_fd()); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + + struct ucred received_creds; + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data), + sizeof(sent_data1))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + + struct ucred want_creds { + 0, 65534, 65534 + }; + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data), + sizeof(sent_data2))); + + EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(sent_data2))); + + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixStreamSocketPairTest, CredsNotCoalescedDown) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + SetSoPassCred(sockets->second_fd()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + UnsetSoPassCred(sockets->second_fd()); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + struct ucred received_creds; + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data), + sizeof(sent_data1))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + + struct ucred want_creds; + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data), + sizeof(sent_data2))); + + EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(sent_data2))); + + want_creds = {0, 65534, 65534}; + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixStreamSocketPairTest, CoalescedCredsNoPasscred) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + SetSoPassCred(sockets->second_fd()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + UnsetSoPassCred(sockets->second_fd()); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), + sizeof(sent_data2))); +} + +TEST_P(UnixStreamSocketPairTest, CoalescedCreds1) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + SetSoPassCred(sockets->second_fd()); + + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + struct ucred received_creds; + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), + sizeof(sent_data2))); + + struct ucred want_creds { + 0, 65534, 65534 + }; + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixStreamSocketPairTest, CoalescedCreds2) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + SetSoPassCred(sockets->second_fd()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; + struct ucred received_creds; + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, + received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), + sizeof(sent_data2))); + + struct ucred want_creds; + ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds.pid, received_creds.pid); + EXPECT_EQ(want_creds.uid, received_creds.uid); + EXPECT_EQ(want_creds.gid, received_creds.gid); +} + +TEST_P(UnixStreamSocketPairTest, NonCoalescedDifferingCreds1) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + SetSoPassCred(sockets->second_fd()); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + char received_data1[sizeof(sent_data1) + sizeof(sent_data2)]; + struct ucred received_creds1; + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds1, + received_data1, sizeof(sent_data1))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1))); + + struct ucred want_creds1 { + 0, 65534, 65534 + }; + + EXPECT_EQ(want_creds1.pid, received_creds1.pid); + EXPECT_EQ(want_creds1.uid, received_creds1.uid); + EXPECT_EQ(want_creds1.gid, received_creds1.gid); + + char received_data2[sizeof(sent_data1) + sizeof(sent_data2)]; + struct ucred received_creds2; + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds2, + received_data2, sizeof(sent_data2))); + + EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2))); + + struct ucred want_creds2; + ASSERT_THAT(want_creds2.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds2.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds2.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds2.pid, received_creds2.pid); + EXPECT_EQ(want_creds2.uid, received_creds2.uid); + EXPECT_EQ(want_creds2.gid, received_creds2.gid); +} + +TEST_P(UnixStreamSocketPairTest, NonCoalescedDifferingCreds2) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + SetSoPassCred(sockets->second_fd()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + UnsetSoPassCred(sockets->second_fd()); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + SetSoPassCred(sockets->second_fd()); + + char received_data1[sizeof(sent_data1) + sizeof(sent_data2)]; + struct ucred received_creds1; + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds1, + received_data1, sizeof(sent_data1))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1))); + + struct ucred want_creds1; + ASSERT_THAT(want_creds1.pid = getpid(), SyscallSucceeds()); + ASSERT_THAT(want_creds1.uid = getuid(), SyscallSucceeds()); + ASSERT_THAT(want_creds1.gid = getgid(), SyscallSucceeds()); + + EXPECT_EQ(want_creds1.pid, received_creds1.pid); + EXPECT_EQ(want_creds1.uid, received_creds1.uid); + EXPECT_EQ(want_creds1.gid, received_creds1.gid); + + char received_data2[sizeof(sent_data1) + sizeof(sent_data2)]; + struct ucred received_creds2; + + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds2, + received_data2, sizeof(sent_data2))); + + EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2))); + + struct ucred want_creds2 { + 0, 65534, 65534 + }; + + EXPECT_EQ(want_creds2.pid, received_creds2.pid); + EXPECT_EQ(want_creds2.uid, received_creds2.uid); + EXPECT_EQ(want_creds2.gid, received_creds2.gid); +} + +TEST_P(UnixStreamSocketPairTest, CoalescedDifferingCreds) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + SetSoPassCred(sockets->second_fd()); + + char sent_data1[20]; + RandomizeBuffer(sent_data1, sizeof(sent_data1)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), + SyscallSucceedsWithValue(sizeof(sent_data1))); + + char sent_data2[20]; + RandomizeBuffer(sent_data2, sizeof(sent_data2)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), + SyscallSucceedsWithValue(sizeof(sent_data2))); + + UnsetSoPassCred(sockets->second_fd()); + + char sent_data3[20]; + RandomizeBuffer(sent_data3, sizeof(sent_data3)); + + ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data3, sizeof(sent_data3)), + SyscallSucceedsWithValue(sizeof(sent_data3))); + + char received_data[sizeof(sent_data1) + sizeof(sent_data2) + + sizeof(sent_data3)]; + + ASSERT_NO_FATAL_FAILURE( + RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); + + EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); + EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), + sizeof(sent_data2))); + EXPECT_EQ(0, memcmp(sent_data3, + received_data + sizeof(sent_data1) + sizeof(sent_data2), + sizeof(sent_data3))); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnixStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(VecCat( + ApplyVec( + UnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + FilesystemBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractBoundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))))); + +// Test fixture for tests that apply to pairs of unbound unix stream sockets. +using UnboundUnixStreamSocketPairTest = SocketPairTest; + +TEST_P(UnboundUnixStreamSocketPairTest, SendtoWithoutConnect) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + char data = 'a'; + ASSERT_THAT(sendto(sockets->second_fd(), &data, sizeof(data), 0, + sockets->first_addr(), sockets->first_addr_size()), + SyscallFailsWithErrno(EOPNOTSUPP)); +} + +TEST_P(UnboundUnixStreamSocketPairTest, SendtoWithoutConnectIgnoresAddr) { + // FIXME: gVisor tries to find /foo/bar and thus returns ENOENT. + if (IsRunningOnGvisor()) { + return; + } + + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), + sockets->first_addr_size()), + SyscallSucceeds()); + + // Even a bogus address is completely ignored. + constexpr char kPath[] = "/foo/bar"; + + // Sanity check that kPath doesn't exist. + struct stat s; + ASSERT_THAT(stat(kPath, &s), SyscallFailsWithErrno(ENOENT)); + + struct sockaddr_un addr = {}; + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, kPath, sizeof(kPath)); + + char data = 'a'; + ASSERT_THAT( + sendto(sockets->second_fd(), &data, sizeof(data), 0, + reinterpret_cast(&addr), sizeof(addr)), + SyscallFailsWithErrno(EOPNOTSUPP)); +} + +INSTANTIATE_TEST_CASE_P( + AllUnixDomainSockets, UnboundUnixStreamSocketPairTest, + ::testing::ValuesIn(IncludeReversals(VecCat( + ApplyVec( + FilesystemUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})), + ApplyVec( + AbstractUnboundUnixDomainSocketPair, + AllBitwiseCombinations(List{SOCK_STREAM}, + List{0, SOCK_NONBLOCK}, + List{0, SOCK_CLOEXEC})))))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc new file mode 100644 index 000000000..aea19dbff --- /dev/null +++ b/test/syscalls/linux/stat.cc @@ -0,0 +1,410 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "test/syscalls/linux/file_base.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class StatTest : public FileTest {}; + +TEST_F(StatTest, FstatatAbs) { + struct stat st; + + // Check that the stat works. + EXPECT_THAT(fstatat(AT_FDCWD, test_file_name_.c_str(), &st, 0), + SyscallSucceeds()); + EXPECT_TRUE(S_ISREG(st.st_mode)); +} + +TEST_F(StatTest, FstatatEmptyPath) { + struct stat st; + const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); + + // Check that the stat works. + EXPECT_THAT(fstatat(fd.get(), "", &st, AT_EMPTY_PATH), SyscallSucceeds()); + EXPECT_TRUE(S_ISREG(st.st_mode)); +} + +TEST_F(StatTest, FstatatRel) { + struct stat st; + int dirfd; + auto filename = std::string(Basename(test_file_name_)); + + // Open the temporary directory read-only. + ASSERT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY), + SyscallSucceeds()); + + // Check that the stat works. + EXPECT_THAT(fstatat(dirfd, filename.c_str(), &st, 0), SyscallSucceeds()); + EXPECT_TRUE(S_ISREG(st.st_mode)); + close(dirfd); +} + +TEST_F(StatTest, FstatatSymlink) { + struct stat st; + + // Check that the link is followed. + EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, 0), SyscallSucceeds()); + EXPECT_TRUE(S_ISDIR(st.st_mode)); + EXPECT_FALSE(S_ISLNK(st.st_mode)); + + // Check that the flag works. + EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, AT_SYMLINK_NOFOLLOW), + SyscallSucceeds()); + EXPECT_TRUE(S_ISLNK(st.st_mode)); + EXPECT_FALSE(S_ISDIR(st.st_mode)); +} + +TEST_F(StatTest, Nlinks) { + TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + // Directory is initially empty, it should contain 2 links (one from itself, + // one from "."). + EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); + + // Create a file in the test directory. Files shouldn't increase the link + // count on the base directory. + TempPath file1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); + EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); + + // Create subdirectories. This should increase the link count by 1 per + // subdirectory. + TempPath dir1 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); + EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3)); + TempPath dir2 = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); + EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(4)); + + // Removing directories should reduce the link count. + dir1.reset(); + EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3)); + dir2.reset(); + EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); + + // Removing files should have no effect on link count. + file1.reset(); + EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); +} + +TEST_F(StatTest, BlocksIncreaseOnWrite) { + struct stat st; + + // Stat the empty file. + ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds()); + + const int initial_blocks = st.st_blocks; + + // Write to the file, making sure to exceed the block size. + std::vector buf(2 * st.st_blksize, 'a'); + ASSERT_THAT(write(test_file_fd_.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + // Stat the file again, and verify that number of allocated blocks has + // increased. + ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds()); + EXPECT_GT(st.st_blocks, initial_blocks); +} + +TEST_F(StatTest, PathNotCleaned) { + TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + // Create a file in the basedir. + TempPath file = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); + + // Stating the file directly should succeed. + struct stat buf; + EXPECT_THAT(lstat(file.path().c_str(), &buf), SyscallSucceeds()); + + // Try to stat the file using a directory that does not exist followed by + // "..". If the path is cleaned prior to stating (which it should not be) + // then this will succeed. + const std::string bad_path = JoinPath("/does_not_exist/..", file.path()); + EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOENT)); +} + +TEST_F(StatTest, PathCanContainDotDot) { + TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + TempPath subdir = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); + const std::string subdir_name = std::string(Basename(subdir.path())); + + // Create a file in the subdir. + TempPath file = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(subdir.path())); + const std::string file_name = std::string(Basename(file.path())); + + // Stat the file through a path that includes '..' and '.' but still resolves + // to the file. + const std::string good_path = + JoinPath(basedir.path(), subdir_name, "..", subdir_name, ".", file_name); + struct stat buf; + EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds()); +} + +TEST_F(StatTest, PathCanContainEmptyComponent) { + TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + // Create a file in the basedir. + TempPath file = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); + const std::string file_name = std::string(Basename(file.path())); + + // Stat the file through a path that includes an empty component. We have to + // build this ourselves because JoinPath automatically removes empty + // components. + const std::string good_path = absl::StrCat(basedir.path(), "//", file_name); + struct stat buf; + EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds()); +} + +TEST_F(StatTest, TrailingSlashNotCleanedReturnsENOTDIR) { + TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + // Create a file in the basedir. + TempPath file = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); + + // Stat the file with an extra "/" on the end of it. Since file is not a + // directory, this should return ENOTDIR. + const std::string bad_path = absl::StrCat(file.path(), "/"); + struct stat buf; + EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOTDIR)); +} + +TEST_F(StatTest, LeadingDoubleSlash) { + // Create a file, and make sure we can stat it. + TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + struct stat st; + ASSERT_THAT(lstat(file.path().c_str(), &st), SyscallSucceeds()); + + // Now add an extra leading slash. + const std::string double_slash_path = absl::StrCat("/", file.path()); + ASSERT_TRUE(absl::StartsWith(double_slash_path, "//")); + + // We should be able to stat the new path, and it should resolve to the same + // file (same device and inode). + struct stat double_slash_st; + ASSERT_THAT(lstat(double_slash_path.c_str(), &double_slash_st), + SyscallSucceeds()); + EXPECT_EQ(st.st_dev, double_slash_st.st_dev); + EXPECT_EQ(st.st_ino, double_slash_st.st_ino); +} + +// Test that a rename doesn't change the underlying file. +TEST_F(StatTest, StatDoesntChangeAfterRename) { + const TempPath old_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath new_path(NewTempAbsPath()); + + struct stat st_old = {}; + struct stat st_new = {}; + + ASSERT_THAT(stat(old_dir.path().c_str(), &st_old), SyscallSucceeds()); + ASSERT_THAT(rename(old_dir.path().c_str(), new_path.path().c_str()), + SyscallSucceeds()); + ASSERT_THAT(stat(new_path.path().c_str(), &st_new), SyscallSucceeds()); + + EXPECT_EQ(st_old.st_nlink, st_new.st_nlink); + EXPECT_EQ(st_old.st_dev, st_new.st_dev); + EXPECT_EQ(st_old.st_ino, st_new.st_ino); + EXPECT_EQ(st_old.st_mode, st_new.st_mode); + EXPECT_EQ(st_old.st_uid, st_new.st_uid); + EXPECT_EQ(st_old.st_gid, st_new.st_gid); + EXPECT_EQ(st_old.st_size, st_new.st_size); +} + +// Test link counts with a regular file as the child. +TEST_F(StatTest, LinkCountsWithRegularFileChild) { + const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + struct stat st_parent_before = {}; + ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds()); + EXPECT_EQ(st_parent_before.st_nlink, 2); + + // Adding a regular file doesn't adjust the parent's link count. + const TempPath child = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); + + struct stat st_parent_after = {}; + ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); + EXPECT_EQ(st_parent_after.st_nlink, 2); + + // The child should have a single link from the parent. + struct stat st_child = {}; + ASSERT_THAT(stat(child.path().c_str(), &st_child), SyscallSucceeds()); + EXPECT_TRUE(S_ISREG(st_child.st_mode)); + EXPECT_EQ(st_child.st_nlink, 1); + + // Finally unlinking the child should not affect the parent's link count. + ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds()); + ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); + EXPECT_EQ(st_parent_after.st_nlink, 2); +} + +// This test verifies that inodes remain around when there is an open fd +// after link count hits 0. +TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild) { + // Setting the enviornment variable GVISOR_GOFER_UNCACHED to any value + // will prevent this test from running, see the tmpfs lifecycle. + // + // We need to support this because when a file is unlinked and we forward + // the stat to the gofer it would return ENOENT. + const char* uncached_gofer = getenv("GVISOR_GOFER_UNCACHED"); + SKIP_IF(uncached_gofer != nullptr); + + // We don't support saving unlinked files. + const DisableSave ds; + + const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const TempPath child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + dir.path(), "hello", TempPath::kDefaultFileMode)); + + // The child should have a single link from the parent. + struct stat st_child_before = {}; + ASSERT_THAT(stat(child.path().c_str(), &st_child_before), SyscallSucceeds()); + EXPECT_TRUE(S_ISREG(st_child_before.st_mode)); + EXPECT_EQ(st_child_before.st_nlink, 1); + EXPECT_EQ(st_child_before.st_size, 5); // Hello is 5 bytes. + + // Open the file so we can fstat after unlinking. + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(child.path(), O_RDONLY)); + + // Now a stat should return ENOENT but we should still be able to stat + // via the open fd and fstat. + ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds()); + + // Since the file has no more links stat should fail. + struct stat st_child_after = {}; + ASSERT_THAT(stat(child.path().c_str(), &st_child_after), + SyscallFailsWithErrno(ENOENT)); + + // Fstat should still allow us to access the same file via the fd. + struct stat st_child_fd = {}; + ASSERT_THAT(fstat(fd.get(), &st_child_fd), SyscallSucceeds()); + EXPECT_EQ(st_child_before.st_dev, st_child_fd.st_dev); + EXPECT_EQ(st_child_before.st_ino, st_child_fd.st_ino); + EXPECT_EQ(st_child_before.st_mode, st_child_fd.st_mode); + EXPECT_EQ(st_child_before.st_uid, st_child_fd.st_uid); + EXPECT_EQ(st_child_before.st_gid, st_child_fd.st_gid); + EXPECT_EQ(st_child_before.st_size, st_child_fd.st_size); + + // TODO: This isn't ideal but since fstatfs(2) will always return + // OVERLAYFS_SUPER_MAGIC we have no way to know if this fs is backed by a + // gofer which doesn't support links. + EXPECT_TRUE(st_child_fd.st_nlink == 0 || st_child_fd.st_nlink == 1); +} + +// Test link counts with a directory as the child. +TEST_F(StatTest, LinkCountsWithDirChild) { + const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + // Before a child is added the two links are "." and the link from the parent. + struct stat st_parent_before = {}; + ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds()); + EXPECT_EQ(st_parent_before.st_nlink, 2); + + // Create a subdirectory and stat for the parent link counts. + const TempPath sub_dir = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); + + // The three links are ".", the link from the parent, and the link from + // the child as "..". + struct stat st_parent_after = {}; + ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); + EXPECT_EQ(st_parent_after.st_nlink, 3); + + // The child will have 1 link from the parent and 1 link which represents ".". + struct stat st_child = {}; + ASSERT_THAT(stat(sub_dir.path().c_str(), &st_child), SyscallSucceeds()); + EXPECT_TRUE(S_ISDIR(st_child.st_mode)); + EXPECT_EQ(st_child.st_nlink, 2); + + // Finally delete the child dir and the parent link count should return to 2. + ASSERT_THAT(rmdir(sub_dir.path().c_str()), SyscallSucceeds()); + ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); + + // Now we should only have links from the parent and "." since the subdir + // has been removed. + EXPECT_EQ(st_parent_after.st_nlink, 2); +} + +// Test statting a child of a non-directory. +TEST_F(StatTest, ChildOfNonDir) { + // Create a path that has a child of a regular file. + const std::string filename = JoinPath(test_file_name_, "child"); + + // Statting the path should return ENOTDIR. + struct stat st; + EXPECT_THAT(lstat(filename.c_str(), &st), SyscallFailsWithErrno(ENOTDIR)); +} + +// Verify that we get an ELOOP from too many symbolic links even when there +// are directories in the middle. +TEST_F(StatTest, LstatELOOPPath) { + const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + std::string subdir_base = "subdir"; + ASSERT_THAT(mkdir(JoinPath(dir.path(), subdir_base).c_str(), 0755), + SyscallSucceeds()); + + std::string target = JoinPath(dir.path(), subdir_base, subdir_base); + std::string dst = JoinPath("..", subdir_base); + ASSERT_THAT(symlink(dst.c_str(), target.c_str()), SyscallSucceeds()); + auto cleanup = Cleanup( + [&target]() { EXPECT_THAT(unlink(target.c_str()), SyscallSucceeds()); }); + + // Now build a path which is /subdir/subdir/... repeated many times so that + // we can build a path that is shorter than PATH_MAX but can still cause + // too many symbolic links. Note: Every other subdir is actually a directory + // so we're not in a situation where it's a -> b -> a -> b, where a and b + // are symbolic links. + std::string path = dir.path(); + std::string subdir_append = absl::StrCat("/", subdir_base); + do { + absl::StrAppend(&path, subdir_append); + // Keep appending /subdir until we would overflow PATH_MAX. + } while ((path.size() + subdir_append.size()) < PATH_MAX); + + struct stat s = {}; + ASSERT_THAT(lstat(path.c_str(), &s), SyscallFailsWithErrno(ELOOP)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/stat_times.cc b/test/syscalls/linux/stat_times.cc new file mode 100644 index 000000000..442957c65 --- /dev/null +++ b/test/syscalls/linux/stat_times.cc @@ -0,0 +1,220 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +using ::testing::IsEmpty; +using ::testing::Not; + +class StatTimesTest : public ::testing::Test { + protected: + std::tuple GetTime(const TempPath& file) { + struct stat statbuf = {}; + EXPECT_THAT(stat(file.path().c_str(), &statbuf), SyscallSucceeds()); + + const auto atime = absl::TimeFromTimespec(statbuf.st_atim); + const auto mtime = absl::TimeFromTimespec(statbuf.st_mtim); + const auto ctime = absl::TimeFromTimespec(statbuf.st_ctim); + return std::make_tuple(atime, mtime, ctime); + } +}; + +TEST_F(StatTimesTest, FileCreationTimes) { + const DisableSave ds; // Timing-related test. + + // Get a time for when the file is created. + const absl::Time before = absl::Now() - absl::Seconds(1); + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const absl::Time after = absl::Now() + absl::Seconds(1); + + absl::Time atime, mtime, ctime; + std::tie(atime, mtime, ctime) = GetTime(file); + + EXPECT_LE(before, atime); + EXPECT_LE(before, mtime); + EXPECT_LE(before, ctime); + EXPECT_GE(after, atime); + EXPECT_GE(after, mtime); + EXPECT_GE(after, ctime); +} + +TEST_F(StatTimesTest, FileCtimeChanges) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + MaybeSave(); // FIXME: ctime is inconsistent. + + absl::Time atime, mtime, ctime; + std::tie(atime, mtime, ctime) = GetTime(file); + + absl::SleepFor(absl::Seconds(1)); + + // Chmod should only change ctime. + EXPECT_THAT(chmod(file.path().c_str(), 0666), SyscallSucceeds()); + + absl::Time atime2, mtime2, ctime2; + std::tie(atime2, mtime2, ctime2) = GetTime(file); + EXPECT_EQ(atime2, atime); + EXPECT_EQ(mtime2, mtime); + EXPECT_GT(ctime2, ctime); + + absl::SleepFor(absl::Seconds(1)); + + // Rename should only change ctime. + const auto newpath = NewTempAbsPath(); + EXPECT_THAT(rename(file.path().c_str(), newpath.c_str()), SyscallSucceeds()); + file.reset(newpath); + + std::tie(atime, mtime, ctime) = GetTime(file); + EXPECT_EQ(atime, atime2); + EXPECT_EQ(mtime, mtime2); + EXPECT_GT(ctime, ctime2); + + absl::SleepFor(absl::Seconds(1)); + + // Utimes should only change ctime and the time that we ask to change (atime + // to now in this case). + const absl::Time before = absl::Now() - absl::Seconds(1); + const struct timespec ts[2] = {{0, UTIME_NOW}, {0, UTIME_OMIT}}; + ASSERT_THAT(utimensat(AT_FDCWD, file.path().c_str(), ts, 0), + SyscallSucceeds()); + const absl::Time after = absl::Now() + absl::Seconds(1); + + std::tie(atime2, mtime2, ctime2) = GetTime(file); + EXPECT_LE(before, atime2); + EXPECT_GE(after, atime2); + EXPECT_EQ(mtime2, mtime); + EXPECT_GT(ctime2, ctime); +} + +TEST_F(StatTimesTest, FileMtimeChanges) { + const auto file = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "yaaass", 0666)); + + absl::Time atime, mtime, ctime; + std::tie(atime, mtime, ctime) = GetTime(file); + + absl::SleepFor(absl::Seconds(1)); + + // Truncate should only change mtime and ctime. + EXPECT_THAT(truncate(file.path().c_str(), 0), SyscallSucceeds()); + + absl::Time atime2, mtime2, ctime2; + std::tie(atime2, mtime2, ctime2) = GetTime(file); + EXPECT_EQ(atime2, atime); + EXPECT_GT(mtime2, mtime); + EXPECT_GT(ctime2, ctime); + + absl::SleepFor(absl::Seconds(1)); + + // Write should only change mtime and ctime. + const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0)); + const std::string contents = "all the single dollars"; + EXPECT_THAT(write(fd.get(), contents.data(), contents.size()), + SyscallSucceeds()); + + std::tie(atime, mtime, ctime) = GetTime(file); + EXPECT_EQ(atime, atime2); + EXPECT_GT(mtime, mtime2); + EXPECT_GT(ctime, ctime2); +} + +TEST_F(StatTimesTest, FileAtimeChanges) { + const std::string contents = "bills bills bills"; + const auto file = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), contents, 0666)); + + MaybeSave(); // FIXME: ctime is inconsistent. + + absl::Time atime, mtime, ctime; + std::tie(atime, mtime, ctime) = GetTime(file); + + absl::SleepFor(absl::Seconds(1)); + + const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY, 0)); + + // Read should only change atime. + char buf[20]; + const absl::Time before = absl::Now() - absl::Seconds(1); + int read_result; + ASSERT_THAT(read_result = read(fd.get(), buf, sizeof(buf)), + SyscallSucceeds()); + const absl::Time after = absl::Now() + absl::Seconds(1); + + EXPECT_EQ(std::string(buf, read_result), contents); + + absl::Time atime2, mtime2, ctime2; + std::tie(atime2, mtime2, ctime2) = GetTime(file); + + EXPECT_LE(before, atime2); + EXPECT_GE(after, atime2); + EXPECT_GT(atime2, atime); + EXPECT_EQ(mtime2, mtime); + EXPECT_EQ(ctime2, ctime); +} + +TEST_F(StatTimesTest, DirAtimeChanges) { + const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const auto file = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); + + MaybeSave(); // FIXME: ctime is inconsistent. + + absl::Time atime, mtime, ctime; + std::tie(atime, mtime, ctime) = GetTime(dir); + + absl::SleepFor(absl::Seconds(1)); + + const absl::Time before = absl::Now() - absl::Seconds(1); + + // NOTE: Keep an fd open. This ensures that the inode backing the + // directory won't be destroyed before the final GetTime to avoid writing out + // timestamps and causing side effects. + const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0)); + + // Listing the directory contents should only change atime. + auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), false)); + EXPECT_THAT(contents, Not(IsEmpty())); + + const absl::Time after = absl::Now() + absl::Seconds(1); + + absl::Time atime2, mtime2, ctime2; + std::tie(atime2, mtime2, ctime2) = GetTime(dir); + + EXPECT_LE(before, atime2); + EXPECT_GE(after, atime2); + EXPECT_GT(atime2, atime); + EXPECT_EQ(mtime2, mtime); + EXPECT_EQ(ctime2, ctime); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/statfs.cc b/test/syscalls/linux/statfs.cc new file mode 100644 index 000000000..1fc9758c9 --- /dev/null +++ b/test/syscalls/linux/statfs.cc @@ -0,0 +1,81 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(StatfsTest, CannotStatBadPath) { + auto temp_file = NewTempAbsPathInDir("/tmp"); + + struct statfs st; + EXPECT_THAT(statfs(temp_file.c_str(), &st), SyscallFailsWithErrno(ENOENT)); +} + +TEST(StatfsTest, InternalTmpfs) { + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + struct statfs st; + EXPECT_THAT(statfs(temp_file.path().c_str(), &st), SyscallSucceeds()); +} + +TEST(StatfsTest, InternalDevShm) { + struct statfs st; + EXPECT_THAT(statfs("/dev/shm", &st), SyscallSucceeds()); +} + +TEST(StatfsTest, NameLen) { + struct statfs st; + EXPECT_THAT(statfs("/dev/shm", &st), SyscallSucceeds()); + + EXPECT_EQ(st.f_namelen, NAME_MAX); +} + +TEST(FstatfsTest, CannotStatBadFd) { + struct statfs st; + EXPECT_THAT(fstatfs(-1, &st), SyscallFailsWithErrno(EBADF)); +} + +TEST(FstatfsTest, InternalTmpfs) { + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY)); + + struct statfs st; + EXPECT_THAT(fstatfs(fd.get(), &st), SyscallSucceeds()); +} + +TEST(FstatfsTest, InternalDevShm) { + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/shm", O_RDONLY)); + + struct statfs st; + EXPECT_THAT(fstatfs(fd.get(), &st), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sticky.cc b/test/syscalls/linux/sticky.cc new file mode 100644 index 000000000..563763d10 --- /dev/null +++ b/test/syscalls/linux/sticky.cc @@ -0,0 +1,116 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_int32(scratch_uid1, 65534, "first scratch UID"); +DEFINE_int32(scratch_gid, 65534, "first scratch GID"); + +namespace gvisor { +namespace testing { + +namespace { + +TEST(StickyTest, StickyBitPermDenied) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(chmod(dir.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); + std::string path = JoinPath(dir.path(), "NewDir"); + ASSERT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + + // Drop privileges and change IDs only in child thread, or else this parent + // thread won't be able to open some log files after the test ends. + ScopedThread([&] { + // Drop privileges. + if (HaveCapability(CAP_FOWNER).ValueOrDie()) { + EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, false)); + } + + // Change EUID and EGID. + EXPECT_THAT(syscall(SYS_setresgid, -1, FLAGS_scratch_gid, -1), + SyscallSucceeds()); + EXPECT_THAT(syscall(SYS_setresuid, -1, FLAGS_scratch_uid1, -1), + SyscallSucceeds()); + + EXPECT_THAT(rmdir(path.c_str()), SyscallFailsWithErrno(EPERM)); + }); +} + +TEST(StickyTest, StickyBitSameUID) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(chmod(dir.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); + std::string path = JoinPath(dir.path(), "NewDir"); + ASSERT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + + // Drop privileges and change IDs only in child thread, or else this parent + // thread won't be able to open some log files after the test ends. + ScopedThread([&] { + // Drop privileges. + if (HaveCapability(CAP_FOWNER).ValueOrDie()) { + EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, false)); + } + + // Change EGID. + EXPECT_THAT(syscall(SYS_setresgid, -1, FLAGS_scratch_gid, -1), + SyscallSucceeds()); + + // We still have the same EUID. + EXPECT_THAT(rmdir(path.c_str()), SyscallSucceeds()); + }); +} + +TEST(StickyTest, StickyBitCapFOWNER) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(chmod(dir.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); + std::string path = JoinPath(dir.path(), "NewDir"); + ASSERT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + + // Drop privileges and change IDs only in child thread, or else this parent + // thread won't be able to open some log files after the test ends. + ScopedThread([&] { + // Set PR_SET_KEEPCAPS. + EXPECT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); + + // Change EUID and EGID. + EXPECT_THAT(syscall(SYS_setresgid, -1, FLAGS_scratch_gid, -1), + SyscallSucceeds()); + EXPECT_THAT(syscall(SYS_setresuid, -1, FLAGS_scratch_uid1, -1), + SyscallSucceeds()); + + EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, true)); + EXPECT_THAT(rmdir(path.c_str()), SyscallSucceeds()); + }); +} +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/symlink.cc b/test/syscalls/linux/symlink.cc new file mode 100644 index 000000000..cfc87bc8f --- /dev/null +++ b/test/syscalls/linux/symlink.cc @@ -0,0 +1,288 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +mode_t FilePermission(const std::string& path) { + struct stat buf = {0}; + TEST_CHECK(lstat(path.c_str(), &buf) == 0); + return buf.st_mode & 0777; +} + +// Test that name collisions are checked on the new link path, not the source +// path. +TEST(SymlinkTest, CanCreateSymlinkWithCachedSourceDirent) { + const std::string srcname = NewTempAbsPath(); + const std::string newname = NewTempAbsPath(); + const std::string basedir = std::string(Dirname(srcname)); + ASSERT_EQ(basedir, Dirname(newname)); + + ASSERT_THAT(chdir(basedir.c_str()), SyscallSucceeds()); + + // Open the source node to cause the underlying dirent to be cached. It will + // remain cached while we have the file open. + int fd; + ASSERT_THAT(fd = open(srcname.c_str(), O_CREAT | O_RDWR, 0666), + SyscallSucceeds()); + FileDescriptor fd_closer(fd); + + // Attempt to create a symlink. If the bug exists, this will fail since the + // dirent link creation code will check for a name collision on the source + // link name. + EXPECT_THAT(symlink(std::string(Basename(srcname)).c_str(), + std::string(Basename(newname)).c_str()), + SyscallSucceeds()); +} + +TEST(SymlinkTest, CanCreateSymlinkFile) { + const std::string oldname = NewTempAbsPath(); + const std::string newname = NewTempAbsPath(); + + int fd; + ASSERT_THAT(fd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), SyscallSucceeds()); + EXPECT_EQ(FilePermission(newname), 0777); + + auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(newname)); + EXPECT_EQ(oldname, link); + + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, CanCreateSymlinkDir) { + const std::string olddir = NewTempAbsPath(); + const std::string newdir = NewTempAbsPath(); + + EXPECT_THAT(mkdir(olddir.c_str(), 0777), SyscallSucceeds()); + EXPECT_THAT(symlink(olddir.c_str(), newdir.c_str()), SyscallSucceeds()); + EXPECT_EQ(FilePermission(newdir), 0777); + + auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(newdir)); + EXPECT_EQ(olddir, link); + + EXPECT_THAT(unlink(newdir.c_str()), SyscallSucceeds()); + + ASSERT_THAT(rmdir(olddir.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, CannotCreateSymlinkInReadOnlyDir) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + const std::string olddir = NewTempAbsPath(); + ASSERT_THAT(mkdir(olddir.c_str(), 0444), SyscallSucceeds()); + + const std::string newdir = NewTempAbsPathInDir(olddir); + EXPECT_THAT(symlink(olddir.c_str(), newdir.c_str()), + SyscallFailsWithErrno(EACCES)); + + ASSERT_THAT(rmdir(olddir.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, CannotSymlinkOverExistingFile) { + const std::string oldname = NewTempAbsPath(); + const std::string newname = NewTempAbsPath(); + + int oldfd; + int newfd; + ASSERT_THAT(oldfd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(oldfd), SyscallSucceeds()); + ASSERT_THAT(newfd = open(newname.c_str(), O_CREAT | O_RDWR, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(newfd), SyscallSucceeds()); + + EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), + SyscallFailsWithErrno(EEXIST)); + + EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, OldnameIsEmpty) { + const std::string newname = NewTempAbsPath(); + EXPECT_THAT(symlink("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); +} + +TEST(SymlinkTest, OldnameIsDangling) { + const std::string newname = NewTempAbsPath(); + EXPECT_THAT(symlink("/dangling", newname.c_str()), SyscallSucceeds()); + + // This is required for S/R random save tests, which pre-run this test + // in the same TEST_TMPDIR, which means that we need to clean it for any + // operations exclusively creating files, like symlink above. + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, NewnameCannotExist) { + const std::string newname = + JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo"); + EXPECT_THAT(symlink("/thisdoesnotmatter", newname.c_str()), + SyscallFailsWithErrno(ENOENT)); +} + +TEST(SymlinkTest, CanEvaluateLink) { + const std::string oldname = NewTempAbsPath(); + const std::string newname = NewTempAbsPath(); + + int fd; + ASSERT_THAT(fd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), + SyscallSucceeds()); + struct stat old; + EXPECT_THAT(fstat(fd, &old), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), SyscallSucceeds()); + EXPECT_EQ(FilePermission(newname), 0777); + + EXPECT_THAT(fd = open(newname.c_str(), O_RDWR, 0666), SyscallSucceeds()); + struct stat old_linked; + EXPECT_THAT(fstat(fd, &old_linked), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + // Check that in fact newname points to the file we expect. + // FIXME: use only inodes here once they are consistent, + // but this is better than nothing. + EXPECT_EQ(old.st_dev, old_linked.st_dev); + EXPECT_EQ(old.st_mode, old_linked.st_mode); + EXPECT_EQ(old.st_size, old_linked.st_size); + + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, TargetIsNotMapped) { + const std::string oldname = NewTempAbsPath(); + const std::string newname = NewTempAbsPath(); + + int fd; + // Create the target so that when we read the link, it exists. + ASSERT_THAT(fd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + // Create a symlink called newname that points to oldname. + EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), SyscallSucceeds()); + + std::vector buf(1024); + int linksize; + // Read the link and assert that the oldname is still the same. + EXPECT_THAT(linksize = readlink(newname.c_str(), buf.data(), 1024), + SyscallSucceeds()); + EXPECT_EQ(0, strncmp(oldname.c_str(), buf.data(), linksize)); + + EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, PreadFromSymlink) { + std::string name = NewTempAbsPath(); + int fd; + ASSERT_THAT(fd = open(name.c_str(), O_CREAT, 0644), SyscallSucceeds()); + ASSERT_THAT(close(fd), SyscallSucceeds()); + + std::string linkname = NewTempAbsPath(); + ASSERT_THAT(symlink(name.c_str(), linkname.c_str()), SyscallSucceeds()); + + ASSERT_THAT(fd = open(linkname.c_str(), O_RDONLY), SyscallSucceeds()); + + char buf[1024]; + EXPECT_THAT(pread64(fd, buf, 1024, 0), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + + EXPECT_THAT(unlink(name.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlink(linkname.c_str()), SyscallSucceeds()); +} + +TEST(SymlinkTest, SymlinkAtDegradedPermissions_NoRandomSave) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); + + int dirfd; + ASSERT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), + SyscallSucceeds()); + + const DisableSave ds; // Permissions are dropped. + EXPECT_THAT(fchmod(dirfd, 0), SyscallSucceeds()); + + std::string basename = std::string(Basename(file.path())); + EXPECT_THAT(symlinkat("/dangling", dirfd, basename.c_str()), + SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(close(dirfd), SyscallSucceeds()); +} + +TEST(SymlinkTest, ReadlinkAtDegradedPermissions_NoRandomSave) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string oldpath = NewTempAbsPathInDir(dir.path()); + const std::string oldbase = std::string(Basename(oldpath)); + ASSERT_THAT(symlink("/dangling", oldpath.c_str()), SyscallSucceeds()); + + int dirfd; + EXPECT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), + SyscallSucceeds()); + + const DisableSave ds; // Permissions are dropped. + EXPECT_THAT(fchmod(dirfd, 0), SyscallSucceeds()); + + char buf[1024]; + int linksize; + EXPECT_THAT(linksize = readlinkat(dirfd, oldbase.c_str(), buf, 1024), + SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(close(dirfd), SyscallSucceeds()); +} + +TEST(SymlinkTest, ChmodSymlink) { + auto target = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string newpath = NewTempAbsPath(); + ASSERT_THAT(symlink(target.path().c_str(), newpath.c_str()), + SyscallSucceeds()); + EXPECT_EQ(FilePermission(newpath), 0777); + EXPECT_THAT(chmod(newpath.c_str(), 0666), SyscallSucceeds()); + EXPECT_EQ(FilePermission(newpath), 0777); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sync.cc b/test/syscalls/linux/sync.cc new file mode 100644 index 000000000..5b777b6eb --- /dev/null +++ b/test/syscalls/linux/sync.cc @@ -0,0 +1,60 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(SyncTest, SyncEverything) { + ASSERT_THAT(syscall(SYS_sync), SyscallSucceeds()); +} + +TEST(SyncTest, SyncFileSytem) { + int fd; + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + ASSERT_THAT(fd = open(f.path().c_str(), O_RDONLY), SyscallSucceeds()); + EXPECT_THAT(syncfs(fd), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(SyncTest, SyncFromPipe) { + int pipes[2]; + EXPECT_THAT(pipe(pipes), SyscallSucceeds()); + EXPECT_THAT(syncfs(pipes[0]), SyscallSucceeds()); + EXPECT_THAT(syncfs(pipes[1]), SyscallSucceeds()); + EXPECT_THAT(close(pipes[0]), SyscallSucceeds()); + EXPECT_THAT(close(pipes[1]), SyscallSucceeds()); +} + +TEST(SyncTest, CannotSyncFileSytemAtBadFd) { + EXPECT_THAT(syncfs(-1), SyscallFailsWithErrno(EBADF)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sync_file_range.cc b/test/syscalls/linux/sync_file_range.cc new file mode 100644 index 000000000..ebe4ca171 --- /dev/null +++ b/test/syscalls/linux/sync_file_range.cc @@ -0,0 +1,111 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(SyncFileRangeTest, TempFileSucceeds) { + auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); + constexpr char data[] = "some data to sync"; + int fd = f.get(); + + EXPECT_THAT(write(fd, data, sizeof(data)), + SyscallSucceedsWithValue(sizeof(data))); + EXPECT_THAT(sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE), + SyscallSucceeds()); + EXPECT_THAT(sync_file_range(fd, 0, 0, 0), SyscallSucceeds()); + EXPECT_THAT( + sync_file_range(fd, 0, 0, + SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER | + SYNC_FILE_RANGE_WAIT_BEFORE), + SyscallSucceeds()); + EXPECT_THAT(sync_file_range( + fd, 0, 1, SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER), + SyscallSucceeds()); + EXPECT_THAT(sync_file_range( + fd, 1, 0, SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER), + SyscallSucceeds()); +} + +TEST(SyncFileRangeTest, CannotSyncFileRangeOnUnopenedFd) { + auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); + constexpr char data[] = "some data to sync"; + int fd = f.get(); + + EXPECT_THAT(write(fd, data, sizeof(data)), + SyscallSucceedsWithValue(sizeof(data))); + + pid_t pid = fork(); + if (pid == 0) { + f.reset(); + + // fd is now invalid. + TEST_CHECK(sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE) == -1); + TEST_PCHECK(errno == EBADF); + _exit(0); + } + + int status = 0; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(WEXITSTATUS(status), 0); +} + +TEST(SyncFileRangeTest, BadArgs) { + auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); + int fd = f.get(); + + EXPECT_THAT(sync_file_range(fd, -1, 0, 0), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(sync_file_range(fd, 0, -1, 0), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(sync_file_range(fd, 8912, INT64_MAX - 4096, 0), + SyscallFailsWithErrno(EINVAL)); +} + +TEST(SyncFileRangeTest, CannotSyncFileRangeWithWaitBefore) { + auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); + constexpr char data[] = "some data to sync"; + int fd = f.get(); + + EXPECT_THAT(write(fd, data, sizeof(data)), + SyscallSucceedsWithValue(sizeof(data))); + if (IsRunningOnGvisor()) { + EXPECT_THAT(sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WAIT_BEFORE), + SyscallFailsWithErrno(ENOSYS)); + EXPECT_THAT( + sync_file_range(fd, 0, 0, + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE), + SyscallFailsWithErrno(ENOSYS)); + } +} + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sysinfo.cc b/test/syscalls/linux/sysinfo.cc new file mode 100644 index 000000000..a0dd82640 --- /dev/null +++ b/test/syscalls/linux/sysinfo.cc @@ -0,0 +1,86 @@ +// Copyright 2018 Google LLC +// +// 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. + +// This is a very simple sanity test to validate that the sysinfo syscall is +// supported by gvisor and returns sane values. +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(SysinfoTest, SysinfoIsCallable) { + struct sysinfo ignored = {}; + EXPECT_THAT(syscall(SYS_sysinfo, &ignored), SyscallSucceedsWithValue(0)); +} + +TEST(SysinfoTest, EfaultProducedOnBadAddress) { + // Validate that we return EFAULT when a bad address is provided. + // specified by man 2 sysinfo + EXPECT_THAT(syscall(SYS_sysinfo, nullptr), SyscallFailsWithErrno(EFAULT)); +} + +TEST(SysinfoTest, TotalRamSaneValue) { + struct sysinfo s = {}; + EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); + EXPECT_GT(s.totalram, 0); +} + +TEST(SysinfoTest, MemunitSet) { + struct sysinfo s = {}; + EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); + EXPECT_GE(s.mem_unit, 1); +} + +TEST(SysinfoTest, UptimeSaneValue) { + struct sysinfo s = {}; + EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); + EXPECT_GE(s.uptime, 0); +} + +TEST(SysinfoTest, UptimeIncreasingValue) { + struct sysinfo s = {}; + EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); + absl::SleepFor(absl::Seconds(2)); + struct sysinfo s2 = {}; + EXPECT_THAT(sysinfo(&s2), SyscallSucceedsWithValue(0)); + EXPECT_LT(s.uptime, s2.uptime); +} + +TEST(SysinfoTest, FreeRamSaneValue) { + struct sysinfo s = {}; + EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); + EXPECT_GT(s.freeram, 0); + EXPECT_LT(s.freeram, s.totalram); +} + +TEST(SysinfoTest, NumProcsSaneValue) { + struct sysinfo s = {}; + EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); + EXPECT_GT(s.procs, 0); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/syslog.cc b/test/syscalls/linux/syslog.cc new file mode 100644 index 000000000..5bd0d1cc3 --- /dev/null +++ b/test/syscalls/linux/syslog.cc @@ -0,0 +1,51 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr int SYSLOG_ACTION_READ_ALL = 3; +constexpr int SYSLOG_ACTION_SIZE_BUFFER = 10; + +int Syslog(int type, char* buf, int len) { + return syscall(__NR_syslog, type, buf, len); +} + +// Only SYSLOG_ACTION_SIZE_BUFFER and SYSLOG_ACTION_READ_ALL are implemented in +// gVisor. + +TEST(Syslog, Size) { + EXPECT_THAT(Syslog(SYSLOG_ACTION_SIZE_BUFFER, nullptr, 0), SyscallSucceeds()); +} + +TEST(Syslog, ReadAll) { + // There might not be anything to read, so we can't check the write count. + char buf[100]; + EXPECT_THAT(Syslog(SYSLOG_ACTION_READ_ALL, buf, sizeof(buf)), + SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/sysret.cc b/test/syscalls/linux/sysret.cc new file mode 100644 index 000000000..8e10220eb --- /dev/null +++ b/test/syscalls/linux/sysret.cc @@ -0,0 +1,113 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Tests to verify that the behavior of linux and gvisor matches when +// 'sysret' returns to bad (aka non-canonical) %rip or %rsp. +#include +#include + +#include "gtest/gtest.h" +#include "test/util/logging.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr uint64_t kNonCanonicalRip = 0xCCCC000000000000; +constexpr uint64_t kNonCanonicalRsp = 0xFFFF000000000000; + +class SysretTest : public ::testing::Test { + protected: + struct user_regs_struct regs_; + pid_t child_; + + void SetUp() override { + pid_t pid = fork(); + + // Child. + if (pid == 0) { + TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); + MaybeSave(); + TEST_PCHECK(raise(SIGSTOP) == 0); + MaybeSave(); + _exit(0); + } + + // Parent. + int status; + ASSERT_THAT(pid, SyscallSucceeds()); // Might still be < 0. + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); + ASSERT_THAT(ptrace(PTRACE_GETREGS, pid, 0, ®s_), SyscallSucceeds()); + + child_ = pid; + } + + void Detach() { + ASSERT_THAT(ptrace(PTRACE_DETACH, child_, 0, 0), SyscallSucceeds()); + } + + void SetRip(uint64_t newrip) { + regs_.rip = newrip; + ASSERT_THAT(ptrace(PTRACE_SETREGS, child_, 0, ®s_), SyscallSucceeds()); + } + + void SetRsp(uint64_t newrsp) { + regs_.rsp = newrsp; + ASSERT_THAT(ptrace(PTRACE_SETREGS, child_, 0, ®s_), SyscallSucceeds()); + } + + // Wait waits for the child pid and returns the exit status. + int Wait() { + int status; + while (true) { + int rval = wait4(child_, &status, 0, NULL); + if (rval < 0) { + return rval; + } + if (rval == child_) { + return status; + } + } + } +}; + +TEST_F(SysretTest, JustDetach) { + Detach(); + int status = Wait(); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << "status = " << status; +} + +TEST_F(SysretTest, BadRip) { + SetRip(kNonCanonicalRip); + Detach(); + int status = Wait(); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) + << "status = " << status; +} + +TEST_F(SysretTest, BadRsp) { + SetRsp(kNonCanonicalRsp); + Detach(); + int status = Wait(); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGBUS) + << "status = " << status; +} +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc new file mode 100644 index 000000000..e6fe84ded --- /dev/null +++ b/test/syscalls/linux/tcp_socket.cc @@ -0,0 +1,759 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +PosixErrorOr InetLoopbackAddr(int family) { + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + addr.ss_family = family; + switch (family) { + case AF_INET: + reinterpret_cast(&addr)->sin_addr.s_addr = + htonl(INADDR_LOOPBACK); + break; + case AF_INET6: + reinterpret_cast(&addr)->sin6_addr = + in6addr_loopback; + break; + default: + return PosixError(EINVAL, + absl::StrCat("unknown socket family: ", family)); + } + return addr; +} + +// Fixture for tests parameterized by the address family to use (AF_INET and +// AF_INET6) when creating sockets. +class TcpSocketTest : public ::testing::TestWithParam { + protected: + // Creates three sockets that will be used by test cases -- a listener, one + // that connects, and the accepted one. + void SetUp() override; + + // Closes the sockets created by SetUp(). + void TearDown() override; + + // Listening socket. + int listener_ = -1; + + // Socket connected via connect(). + int s_ = -1; + + // Socket connected via accept(). + int t_ = -1; + + // Initial size of the send buffer. + int sendbuf_size_ = -1; +}; + +void TcpSocketTest::SetUp() { + ASSERT_THAT(listener_ = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), + SyscallSucceeds()); + + ASSERT_THAT(s_ = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), + SyscallSucceeds()); + + // Initialize address to the loopback one. + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t addrlen = sizeof(addr); + + // Bind to some port then start listening. + ASSERT_THAT( + bind(listener_, reinterpret_cast(&addr), addrlen), + SyscallSucceeds()); + + ASSERT_THAT(listen(listener_, SOMAXCONN), SyscallSucceeds()); + + // Get the address we're listening on, then connect to it. We need to do this + // because we're allowing the stack to pick a port for us. + ASSERT_THAT(getsockname(listener_, reinterpret_cast(&addr), + &addrlen), + SyscallSucceeds()); + + ASSERT_THAT(RetryEINTR(connect)(s_, reinterpret_cast(&addr), + addrlen), + SyscallSucceeds()); + + // Get the initial send buffer size. + socklen_t optlen = sizeof(sendbuf_size_); + ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &sendbuf_size_, &optlen), + SyscallSucceeds()); + + // Accept the connection. + ASSERT_THAT(t_ = RetryEINTR(accept)(listener_, nullptr, nullptr), + SyscallSucceeds()); +} + +void TcpSocketTest::TearDown() { + EXPECT_THAT(close(listener_), SyscallSucceeds()); + if (s_ >= 0) { + EXPECT_THAT(close(s_), SyscallSucceeds()); + } + if (t_ >= 0) { + EXPECT_THAT(close(t_), SyscallSucceeds()); + } +} + +TEST_P(TcpSocketTest, DataCoalesced) { + char buf[10]; + + // Write in two steps. + ASSERT_THAT(RetryEINTR(write)(s_, buf, sizeof(buf) / 2), + SyscallSucceedsWithValue(sizeof(buf) / 2)); + ASSERT_THAT(RetryEINTR(write)(s_, buf, sizeof(buf) / 2), + SyscallSucceedsWithValue(sizeof(buf) / 2)); + + // Allow stack to process both packets. + absl::SleepFor(absl::Seconds(1)); + + // Read in one shot. + EXPECT_THAT(RetryEINTR(recv)(t_, buf, sizeof(buf), 0), + SyscallSucceedsWithValue(sizeof(buf))); +} + +TEST_P(TcpSocketTest, SenderAddressIgnored) { + char buf[3]; + ASSERT_THAT(RetryEINTR(write)(s_, buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + + ASSERT_THAT( + RetryEINTR(recvfrom)(t_, buf, sizeof(buf), 0, + reinterpret_cast(&addr), &addrlen), + SyscallSucceedsWithValue(3)); + + // Check that addr remains zeroed-out. + const char* ptr = reinterpret_cast(&addr); + for (size_t i = 0; i < sizeof(addr); i++) { + EXPECT_EQ(ptr[i], 0); + } +} + +TEST_P(TcpSocketTest, SenderAddressIgnoredOnPeek) { + char buf[3]; + ASSERT_THAT(RetryEINTR(write)(s_, buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + + ASSERT_THAT( + RetryEINTR(recvfrom)(t_, buf, sizeof(buf), MSG_PEEK, + reinterpret_cast(&addr), &addrlen), + SyscallSucceedsWithValue(3)); + + // Check that addr remains zeroed-out. + const char* ptr = reinterpret_cast(&addr); + for (size_t i = 0; i < sizeof(addr); i++) { + EXPECT_EQ(ptr[i], 0); + } +} + +TEST_P(TcpSocketTest, SendtoAddressIgnored) { + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + addr.ss_family = GetParam(); // FIXME + + char data = '\0'; + EXPECT_THAT( + RetryEINTR(sendto)(s_, &data, sizeof(data), 0, + reinterpret_cast(&addr), sizeof(addr)), + SyscallSucceedsWithValue(1)); +} + +TEST_P(TcpSocketTest, WritevZeroIovec) { + // 2 bytes just to be safe and have vecs[1] not point to something random + // (even though length is 0). + char buf[2]; + char recv_buf[1]; + + // Construct a vec where the final vector is of length 0. + iovec vecs[2] = {}; + vecs[0].iov_base = buf; + vecs[0].iov_len = 1; + vecs[1].iov_base = buf + 1; + vecs[1].iov_len = 0; + + EXPECT_THAT(RetryEINTR(writev)(s_, vecs, 2), SyscallSucceedsWithValue(1)); + + EXPECT_THAT(RetryEINTR(recv)(t_, recv_buf, 1, 0), + SyscallSucceedsWithValue(1)); + EXPECT_EQ(memcmp(recv_buf, buf, 1), 0); +} + +TEST_P(TcpSocketTest, ZeroWriteAllowed) { + char buf[3]; + // Send a zero length packet. + ASSERT_THAT(RetryEINTR(write)(s_, buf, 0), SyscallSucceedsWithValue(0)); + // Verify that there is no packet available. + EXPECT_THAT(RetryEINTR(recv)(t_, buf, sizeof(buf), MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +// Test that a non-blocking write with a buffer that is larger than the send +// buffer size will not actually write the whole thing at once. +TEST_P(TcpSocketTest, NonblockingLargeWrite) { + // Set the FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(s_, F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(s_, F_SETFL, opts), SyscallSucceeds()); + + // Allocate a buffer three times the size of the send buffer. We do this with + // a vector to avoid allocating on the stack. + int size = 3 * sendbuf_size_; + std::vector buf(size); + + // Try to write the whole thing. + int n; + ASSERT_THAT(n = RetryEINTR(write)(s_, buf.data(), size), SyscallSucceeds()); + + // We should have written something, but not the whole thing. + EXPECT_GT(n, 0); + EXPECT_LT(n, size); +} + +// Test that a blocking write with a buffer that is larger than the send buffer +// will block until the entire buffer is sent. +TEST_P(TcpSocketTest, BlockingLargeWrite_NoRandomSave) { + // Allocate a buffer three times the size of the send buffer on the heap. We + // do this as a vector to avoid allocating on the stack. + int size = 3 * sendbuf_size_; + std::vector writebuf(size); + + // Start reading the response in a loop. + int read_bytes = 0; + ScopedThread t([this, &read_bytes]() { + // Avoid interrupting the blocking write in main thread. + const DisableSave ds; + char readbuf[2500] = {}; + int n = -1; + while (n != 0) { + EXPECT_THAT(n = RetryEINTR(read)(t_, &readbuf, sizeof(readbuf)), + SyscallSucceeds()); + read_bytes += n; + } + }); + + // Try to write the whole thing. + int n; + ASSERT_THAT(n = WriteFd(s_, writebuf.data(), size), SyscallSucceeds()); + + // We should have written the whole thing. + EXPECT_EQ(n, size); + EXPECT_THAT(close(s_), SyscallSucceedsWithValue(0)); + s_ = -1; + t.Join(); + + // We should have read the whole thing. + EXPECT_EQ(read_bytes, size); +} + +// Test that a send with MSG_DONTWAIT flag and buffer that larger than the send +// buffer size will not write the whole thing. +TEST_P(TcpSocketTest, LargeSendDontWait) { + // Allocate a buffer three times the size of the send buffer. We do this on + // with a vector to avoid allocating on the stack. + int size = 3 * sendbuf_size_; + std::vector buf(size); + + // Try to write the whole thing with MSG_DONTWAIT flag, which can + // return a partial write. + int n; + ASSERT_THAT(n = RetryEINTR(send)(s_, buf.data(), size, MSG_DONTWAIT), + SyscallSucceeds()); + + // We should have written something, but not the whole thing. + EXPECT_GT(n, 0); + EXPECT_LT(n, size); +} + +// Test that a send on a non-blocking socket with a buffer that larger than the +// send buffer will not write the whole thing at once. +TEST_P(TcpSocketTest, NonblockingLargeSend) { + // Set the FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(s_, F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(s_, F_SETFL, opts), SyscallSucceeds()); + + // Allocate a buffer three times the size of the send buffer. We do this on + // with a vector to avoid allocating on the stack. + int size = 3 * sendbuf_size_; + std::vector buf(size); + + // Try to write the whole thing. + int n; + ASSERT_THAT(n = RetryEINTR(send)(s_, buf.data(), size, 0), SyscallSucceeds()); + + // We should have written something, but not the whole thing. + EXPECT_GT(n, 0); + EXPECT_LT(n, size); +} + +// Same test as above, but calls send instead of write. +TEST_P(TcpSocketTest, BlockingLargeSend_NoRandomSave) { + // Allocate a buffer three times the size of the send buffer. We do this on + // with a vector to avoid allocating on the stack. + int size = 3 * sendbuf_size_; + std::vector writebuf(size); + + // Start reading the response in a loop. + int read_bytes = 0; + ScopedThread t([this, &read_bytes]() { + // Avoid interrupting the blocking write in main thread. + const DisableSave ds; + char readbuf[2500] = {}; + int n = -1; + while (n != 0) { + EXPECT_THAT(n = RetryEINTR(read)(t_, &readbuf, sizeof(readbuf)), + SyscallSucceeds()); + read_bytes += n; + } + }); + + // Try to send the whole thing. + int n; + ASSERT_THAT(n = SendFd(s_, writebuf.data(), size, 0), SyscallSucceeds()); + + // We should have written the whole thing. + EXPECT_EQ(n, size); + EXPECT_THAT(close(s_), SyscallSucceedsWithValue(0)); + s_ = -1; + t.Join(); + + // We should have read the whole thing. + EXPECT_EQ(read_bytes, size); +} + +// Test that polling on a socket with a full send buffer will block. +TEST_P(TcpSocketTest, PollWithFullBufferBlocks) { + // Set the FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(s_, F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(s_, F_SETFL, opts), SyscallSucceeds()); + + // Set TCP_NODELAY, which will cause linux to fill the receive buffer from the + // send buffer as quickly as possibly. This way we can fill up both buffers + // faster. + constexpr int tcp_nodelay_flag = 1; + ASSERT_THAT(setsockopt(s_, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay_flag, + sizeof(tcp_nodelay_flag)), + SyscallSucceeds()); + + // Create a large buffer that will be used for sending. + std::vector buf(5 * sendbuf_size_); + + // Write until we receive an error. + while (RetryEINTR(send)(s_, buf.data(), buf.size(), 0) != -1) { + // Sleep to give linux a chance to move data from the send buffer to the + // receive buffer. + usleep(10000); // 10ms. + } + // The last error should have been EWOULDBLOCK. + ASSERT_EQ(errno, EWOULDBLOCK); +} + +TEST_P(TcpSocketTest, MsgTrunc) { + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(RetryEINTR(send)(s_, sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)] = {}; + ASSERT_THAT( + RetryEINTR(recv)(t_, received_data, sizeof(received_data) / 2, MSG_TRUNC), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + + // Check that we didn't get anything. + char zeros[sizeof(received_data)] = {}; + EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); +} + +// MSG_CTRUNC is a return flag but linux allows it to be set on input flags +// without returning an error. +TEST_P(TcpSocketTest, MsgTruncWithCtrunc) { + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(RetryEINTR(send)(s_, sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)] = {}; + ASSERT_THAT(RetryEINTR(recv)(t_, received_data, sizeof(received_data) / 2, + MSG_TRUNC | MSG_CTRUNC), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + + // Check that we didn't get anything. + char zeros[sizeof(received_data)] = {}; + EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); +} + +// This test will verify that MSG_CTRUNC doesn't do anything when specified +// on input. +TEST_P(TcpSocketTest, MsgTruncWithCtruncOnly) { + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(RetryEINTR(send)(s_, sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)] = {}; + ASSERT_THAT(RetryEINTR(recv)(t_, received_data, sizeof(received_data) / 2, + MSG_CTRUNC), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + + // Since MSG_CTRUNC here had no affect, it should not behave like MSG_TRUNC. + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); +} + +TEST_P(TcpSocketTest, MsgTruncLargeSize) { + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(RetryEINTR(send)(s_, sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data) * 2] = {}; + ASSERT_THAT( + RetryEINTR(recv)(t_, received_data, sizeof(received_data), MSG_TRUNC), + SyscallSucceedsWithValue(sizeof(sent_data))); + + // Check that we didn't get anything. + char zeros[sizeof(received_data)] = {}; + EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); +} + +TEST_P(TcpSocketTest, MsgTruncPeek) { + char sent_data[512]; + RandomizeBuffer(sent_data, sizeof(sent_data)); + ASSERT_THAT(RetryEINTR(send)(s_, sent_data, sizeof(sent_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + char received_data[sizeof(sent_data)] = {}; + ASSERT_THAT(RetryEINTR(recv)(t_, received_data, sizeof(received_data) / 2, + MSG_TRUNC | MSG_PEEK), + SyscallSucceedsWithValue(sizeof(sent_data) / 2)); + + // Check that we didn't get anything. + char zeros[sizeof(received_data)] = {}; + EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); + + // Check that we can still get all of the data. + ASSERT_THAT(RetryEINTR(recv)(t_, received_data, sizeof(received_data), 0), + SyscallSucceedsWithValue(sizeof(sent_data))); + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); +} + +TEST_P(TcpSocketTest, NoDelayDefault) { + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(s_, IPPROTO_TCP, TCP_NODELAY, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOff); +} + +TEST_P(TcpSocketTest, SetNoDelay) { + ASSERT_THAT( + setsockopt(s_, IPPROTO_TCP, TCP_NODELAY, &kSockOptOn, sizeof(kSockOptOn)), + SyscallSucceeds()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(s_, IPPROTO_TCP, TCP_NODELAY, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOn); + + ASSERT_THAT(setsockopt(s_, IPPROTO_TCP, TCP_NODELAY, &kSockOptOff, + sizeof(kSockOptOff)), + SyscallSucceeds()); + + EXPECT_THAT(getsockopt(s_, IPPROTO_TCP, TCP_NODELAY, &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kSockOptOff); +} + +INSTANTIATE_TEST_CASE_P(AllInetTests, TcpSocketTest, + ::testing::Values(AF_INET, AF_INET6)); + +// Fixture for tests parameterized by address family that don't want the fixture +// to do things. +using SimpleTcpSocketTest = ::testing::TestWithParam; + +TEST_P(SimpleTcpSocketTest, SendUnconnected) { + int fd; + ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), + SyscallSucceeds()); + FileDescriptor sock_fd(fd); + + char data = '\0'; + EXPECT_THAT(RetryEINTR(send)(fd, &data, sizeof(data), 0), + SyscallFailsWithErrno(EPIPE)); +} + +TEST_P(SimpleTcpSocketTest, SendtoWithoutAddressUnconnected) { + int fd; + ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), + SyscallSucceeds()); + FileDescriptor sock_fd(fd); + + char data = '\0'; + EXPECT_THAT(RetryEINTR(sendto)(fd, &data, sizeof(data), 0, nullptr, 0), + SyscallFailsWithErrno(EPIPE)); +} + +TEST_P(SimpleTcpSocketTest, SendtoWithAddressUnconnected) { + int fd; + ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), + SyscallSucceeds()); + FileDescriptor sock_fd(fd); + + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + char data = '\0'; + EXPECT_THAT( + RetryEINTR(sendto)(fd, &data, sizeof(data), 0, + reinterpret_cast(&addr), sizeof(addr)), + SyscallFailsWithErrno(EPIPE)); +} + +TEST_P(SimpleTcpSocketTest, GetPeerNameUnconnected) { + int fd; + ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), + SyscallSucceeds()); + FileDescriptor sock_fd(fd); + + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getpeername(fd, reinterpret_cast(&addr), &addrlen), + SyscallFailsWithErrno(ENOTCONN)); +} + +TEST_P(TcpSocketTest, FullBuffer) { + // Set both FDs to be blocking. + int flags = 0; + ASSERT_THAT(flags = fcntl(s_, F_GETFL), SyscallSucceeds()); + EXPECT_THAT(fcntl(s_, F_SETFL, flags & ~O_NONBLOCK), SyscallSucceeds()); + flags = 0; + ASSERT_THAT(flags = fcntl(t_, F_GETFL), SyscallSucceeds()); + EXPECT_THAT(fcntl(t_, F_SETFL, flags & ~O_NONBLOCK), SyscallSucceeds()); + + // 2500 was chosen as a small value that can be set on Linux. + int set_snd = 2500; + EXPECT_THAT(setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &set_snd, sizeof(set_snd)), + SyscallSucceedsWithValue(0)); + int get_snd = -1; + socklen_t get_snd_len = sizeof(get_snd); + EXPECT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &get_snd, &get_snd_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_snd_len, sizeof(get_snd)); + EXPECT_GT(get_snd, 0); + + // 2500 was chosen as a small value that can be set on Linux and gVisor. + int set_rcv = 2500; + EXPECT_THAT(setsockopt(t_, SOL_SOCKET, SO_RCVBUF, &set_rcv, sizeof(set_rcv)), + SyscallSucceedsWithValue(0)); + int get_rcv = -1; + socklen_t get_rcv_len = sizeof(get_rcv); + EXPECT_THAT(getsockopt(t_, SOL_SOCKET, SO_RCVBUF, &get_rcv, &get_rcv_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_rcv_len, sizeof(get_rcv)); + EXPECT_GE(get_rcv, 2500); + + // Quick sanity test. + EXPECT_LT(get_snd + get_rcv, 2500 * IOV_MAX); + + char data[2500] = {}; + std::vector iovecs; + for (int i = 0; i < IOV_MAX; i++) { + struct iovec iov = {}; + iov.iov_base = data; + iov.iov_len = sizeof(data); + iovecs.push_back(iov); + } + ScopedThread t([this, &iovecs]() { + int result = -1; + EXPECT_THAT(result = RetryEINTR(writev)(s_, iovecs.data(), iovecs.size()), + SyscallSucceeds()); + EXPECT_GT(result, 1); + EXPECT_LT(result, sizeof(data) * iovecs.size()); + }); + + char recv = 0; + EXPECT_THAT(RetryEINTR(read)(t_, &recv, 1), SyscallSucceedsWithValue(1)); + EXPECT_THAT(close(t_), SyscallSucceedsWithValue(0)); + t_ = -1; +} + +TEST_P(SimpleTcpSocketTest, NonBlockingConnectNoListener) { + // Initialize address to the loopback one. + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t addrlen = sizeof(addr); + + const FileDescriptor s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + // Set the FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(s.get(), F_SETFL, opts), SyscallSucceeds()); + + ASSERT_THAT(RetryEINTR(connect)( + s.get(), reinterpret_cast(&addr), addrlen), + SyscallFailsWithErrno(EINPROGRESS)); + + // Now polling on the FD with a timeout should return 0 corresponding to no + // FDs ready. + struct pollfd poll_fd = {s.get(), POLLOUT, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000), + SyscallSucceedsWithValue(1)); + + int err; + socklen_t optlen = sizeof(err); + ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), + SyscallSucceeds()); + + EXPECT_EQ(err, ECONNREFUSED); +} + +TEST_P(SimpleTcpSocketTest, NonBlockingConnect) { + const FileDescriptor listener = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + // Initialize address to the loopback one. + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t addrlen = sizeof(addr); + + // Bind to some port then start listening. + ASSERT_THAT( + bind(listener.get(), reinterpret_cast(&addr), addrlen), + SyscallSucceeds()); + + ASSERT_THAT(listen(listener.get(), SOMAXCONN), SyscallSucceeds()); + + FileDescriptor s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + // Set the FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(s.get(), F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(s.get(), F_SETFL, opts), SyscallSucceeds()); + + ASSERT_THAT(getsockname(listener.get(), + reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + + ASSERT_THAT(RetryEINTR(connect)( + s.get(), reinterpret_cast(&addr), addrlen), + SyscallFailsWithErrno(EINPROGRESS)); + + int t; + ASSERT_THAT(t = RetryEINTR(accept)(listener.get(), nullptr, nullptr), + SyscallSucceeds()); + + // Now polling on the FD with a timeout should return 0 corresponding to no + // FDs ready. + struct pollfd poll_fd = {s.get(), POLLOUT, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000), + SyscallSucceedsWithValue(1)); + + int err; + socklen_t optlen = sizeof(err); + ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), + SyscallSucceeds()); + + EXPECT_EQ(err, 0); + + EXPECT_THAT(close(t), SyscallSucceeds()); +} + +// Test that we get an ECONNREFUSED with a blocking socket when no one is +// listening on the other end. +TEST_P(SimpleTcpSocketTest, BlockingConnectRefused) { + FileDescriptor s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + // Initialize address to the loopback one. + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t addrlen = sizeof(addr); + + ASSERT_THAT(RetryEINTR(connect)( + s.get(), reinterpret_cast(&addr), addrlen), + SyscallFailsWithErrno(ECONNREFUSED)); + + // Avoiding triggering save in destructor of s. + EXPECT_THAT(close(s.release()), SyscallSucceeds()); +} + +// Test that we get an ECONNREFUSED with a nonblocking socket. +TEST_P(SimpleTcpSocketTest, NonBlockingConnectRefused) { + FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(GetParam(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); + + // Initialize address to the loopback one. + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t addrlen = sizeof(addr); + + ASSERT_THAT(RetryEINTR(connect)( + s.get(), reinterpret_cast(&addr), addrlen), + SyscallFailsWithErrno(EINPROGRESS)); + + // We don't need to specify any events to get POLLHUP or POLLERR as these + // are added before the poll. + struct pollfd poll_fd = {s.get(), /*events=*/0, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 1000), SyscallSucceedsWithValue(1)); + + // The ECONNREFUSED should cause us to be woken up with POLLHUP. + EXPECT_NE(poll_fd.revents & (POLLHUP | POLLERR), 0); + + // Avoiding triggering save in destructor of s. + EXPECT_THAT(close(s.release()), SyscallSucceeds()); +} + +INSTANTIATE_TEST_CASE_P(AllInetTests, SimpleTcpSocketTest, + ::testing::Values(AF_INET, AF_INET6)); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/temp_umask.h b/test/syscalls/linux/temp_umask.h new file mode 100644 index 000000000..f202dfa59 --- /dev/null +++ b/test/syscalls/linux/temp_umask.h @@ -0,0 +1,39 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_TEMP_UMASK_H_ +#define GVISOR_TEST_SYSCALLS_TEMP_UMASK_H_ + +#include +#include + +namespace gvisor { +namespace testing { + +class TempUmask { + public: + // Sets the process umask to `mask`. + explicit TempUmask(mode_t mask) : old_mask_(umask(mask)) {} + + // Sets the process umask to its previous value. + ~TempUmask() { umask(old_mask_); } + + private: + mode_t old_mask_; +}; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_TEMP_UMASK_H_ diff --git a/test/syscalls/linux/tgkill.cc b/test/syscalls/linux/tgkill.cc new file mode 100644 index 000000000..2d258ef11 --- /dev/null +++ b/test/syscalls/linux/tgkill.cc @@ -0,0 +1,48 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(TgkillTest, InvalidTID) { + EXPECT_THAT(tgkill(getpid(), -1, 0), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(tgkill(getpid(), 0, 0), SyscallFailsWithErrno(EINVAL)); +} + +TEST(TgkillTest, InvalidTGID) { + EXPECT_THAT(tgkill(-1, gettid(), 0), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(tgkill(0, gettid(), 0), SyscallFailsWithErrno(EINVAL)); +} + +TEST(TgkillTest, ValidInput) { + EXPECT_THAT(tgkill(getpid(), gettid(), 0), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/time.cc b/test/syscalls/linux/time.cc new file mode 100644 index 000000000..3abcd8098 --- /dev/null +++ b/test/syscalls/linux/time.cc @@ -0,0 +1,103 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/proc_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr long kFudgeSeconds = 5; + +// Mimics the time(2) wrapper from glibc prior to 2.15. +time_t vsyscall_time(time_t* t) { + constexpr uint64_t kVsyscallTimeEntry = 0xffffffffff600400; + return reinterpret_cast(kVsyscallTimeEntry)(t); +} + +TEST(TimeTest, VsyscallTime_Succeeds) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); + + time_t t1, t2; + + { + const DisableSave ds; // Timing assertions. + EXPECT_THAT(time(&t1), SyscallSucceeds()); + EXPECT_THAT(vsyscall_time(&t2), SyscallSucceeds()); + } + + // Time should be monotonic. + EXPECT_LE(static_cast(t1), static_cast(t2)); + + // Check that it's within kFudge seconds. + EXPECT_LE(static_cast(t2), static_cast(t1) + kFudgeSeconds); + + // Redo with save. + EXPECT_THAT(time(&t1), SyscallSucceeds()); + EXPECT_THAT(vsyscall_time(&t2), SyscallSucceeds()); + + // Time should be monotonic. + EXPECT_LE(static_cast(t1), static_cast(t2)); +} + +TEST(TimeTest, VsyscallTime_InvalidAddressSIGSEGV) { + EXPECT_EXIT(vsyscall_time(reinterpret_cast(0x1)), + ::testing::KilledBySignal(SIGSEGV), ""); +} +int vsyscall_gettimeofday(struct timeval* tv, struct timezone* tz) { + constexpr uint64_t kVsyscallGettimeofdayEntry = 0xffffffffff600000; + return reinterpret_cast( + kVsyscallGettimeofdayEntry)(tv, tz); +} + +TEST(TimeTest, VsyscallGettimeofday_Succeeds) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); + + struct timeval tv1, tv2; + struct timezone tz1, tz2; + + { + const DisableSave ds; // Timing assertions. + EXPECT_THAT(gettimeofday(&tv1, &tz1), SyscallSucceeds()); + EXPECT_THAT(vsyscall_gettimeofday(&tv2, &tz2), SyscallSucceeds()); + } + + // See above. + EXPECT_LE(static_cast(tv1.tv_sec), static_cast(tv2.tv_sec)); + EXPECT_LE(static_cast(tv2.tv_sec), + static_cast(tv1.tv_sec) + kFudgeSeconds); + + // Redo with save. + EXPECT_THAT(gettimeofday(&tv1, &tz1), SyscallSucceeds()); + EXPECT_THAT(vsyscall_gettimeofday(&tv2, &tz2), SyscallSucceeds()); +} + +TEST(TimeTest, VsyscallGettimeofday_InvalidAddressSIGSEGV) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); + + EXPECT_EXIT(vsyscall_gettimeofday(reinterpret_cast(0x1), + reinterpret_cast(0x1)), + ::testing::KilledBySignal(SIGSEGV), ""); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/timerfd.cc b/test/syscalls/linux/timerfd.cc new file mode 100644 index 000000000..b85321795 --- /dev/null +++ b/test/syscalls/linux/timerfd.cc @@ -0,0 +1,238 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Wrapper around timerfd_create(2) that returns a FileDescriptor. +PosixErrorOr TimerfdCreate(int clockid, int flags) { + int fd = timerfd_create(clockid, flags); + MaybeSave(); + if (fd < 0) { + return PosixError(errno, "timerfd_create failed"); + } + return FileDescriptor(fd); +} + +// In tests that race a timerfd with a sleep, some slack is required because: +// +// - Timerfd expirations are asynchronous with respect to nanosleeps. +// +// - Because clock_gettime(CLOCK_MONOTONIC) is implemented through the VDSO, +// it technically uses a closely-related, but distinct, time domain from the +// CLOCK_MONOTONIC used to trigger timerfd expirations. +absl::Duration TimerSlack() { return absl::Milliseconds(500); } + +TEST(TimerfdTest, IsInitiallyStopped) { + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); + struct itimerspec its = {}; + ASSERT_THAT(timerfd_gettime(tfd.get(), &its), SyscallSucceeds()); + EXPECT_EQ(0, its.it_value.tv_sec); + EXPECT_EQ(0, its.it_value.tv_nsec); +} + +TEST(TimerfdTest, SingleShot) { + constexpr absl::Duration kDelay = absl::Seconds(1); + + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); + struct itimerspec its = {}; + its.it_value = absl::ToTimespec(kDelay); + ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), + SyscallSucceeds()); + + // The timer should fire exactly once since the interval is zero. + absl::SleepFor(kDelay + TimerSlack()); + uint64_t val = 0; + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallSucceedsWithValue(sizeof(uint64_t))); + EXPECT_EQ(1, val); +} + +TEST(TimerfdTest, Periodic) { + constexpr absl::Duration kDelay = absl::Seconds(1); + constexpr int kPeriods = 3; + + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); + struct itimerspec its = {}; + its.it_value = absl::ToTimespec(kDelay); + its.it_interval = absl::ToTimespec(kDelay); + ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), + SyscallSucceeds()); + + // Expect to see at least kPeriods expirations. More may occur due to the + // timer slack, or due to delays from scheduling or save/restore. + absl::SleepFor(kPeriods * kDelay + TimerSlack()); + uint64_t val = 0; + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallSucceedsWithValue(sizeof(uint64_t))); + EXPECT_GE(val, kPeriods); +} + +TEST(TimerfdTest, BlockingRead) { + constexpr absl::Duration kDelay = absl::Seconds(3); + + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); + struct itimerspec its = {}; + its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); + auto const start_time = absl::Now(); + ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), + SyscallSucceeds()); + + // read should block until the timer fires. + uint64_t val = 0; + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallSucceedsWithValue(sizeof(uint64_t))); + auto const end_time = absl::Now(); + EXPECT_EQ(1, val); + EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay); +} + +TEST(TimerfdTest, NonblockingRead_NoRandomSave) { + constexpr absl::Duration kDelay = absl::Seconds(5); + + auto const tfd = + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); + + // Since the timer is initially disabled and has never fired, read should + // return EAGAIN. + uint64_t val = 0; + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallFailsWithErrno(EAGAIN)); + + DisableSave ds; // Timing-sensitive. + + // Arm the timer. + struct itimerspec its = {}; + its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); + ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), + SyscallSucceeds()); + + // Since the timer has not yet fired, read should return EAGAIN. + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallFailsWithErrno(EAGAIN)); + + ds.reset(); // No longer timing-sensitive. + + // After the timer fires, read should indicate 1 expiration. + absl::SleepFor(kDelay + TimerSlack()); + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallSucceedsWithValue(sizeof(uint64_t))); + EXPECT_EQ(1, val); + + // The successful read should have reset the number of expirations. + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) { + constexpr absl::Duration kDelay = absl::Seconds(3); + + auto const tfd = + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); + struct itimerspec its = {}; + its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); + auto const start_time = absl::Now(); + ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), + SyscallSucceeds()); + + // poll should block until the timer fires. + struct pollfd pfd = {}; + pfd.fd = tfd.get(); + pfd.events = POLLIN; + ASSERT_THAT(poll(&pfd, /* nfds = */ 1, + /* timeout = */ 2 * absl::ToInt64Seconds(kDelay) * 1000), + SyscallSucceedsWithValue(1)); + auto const end_time = absl::Now(); + EXPECT_EQ(POLLIN, pfd.revents); + EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay); + + // Call timerfd_settime again with a value of 0. This should reset the number + // of expirations to 0, causing read to return EAGAIN since the timerfd is + // non-blocking. + its.it_value.tv_sec = 0; + ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), + SyscallSucceeds()); + uint64_t val = 0; + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(TimerfdTest, SetAbsoluteTime) { + constexpr absl::Duration kDelay = absl::Seconds(3); + + // Use a non-blocking timerfd so that if TFD_TIMER_ABSTIME is incorrectly + // non-functional, we get EAGAIN rather than a test timeout. + auto const tfd = + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); + struct itimerspec its = {}; + ASSERT_THAT(clock_gettime(CLOCK_MONOTONIC, &its.it_value), SyscallSucceeds()); + its.it_value.tv_sec += absl::ToInt64Seconds(kDelay); + ASSERT_THAT(timerfd_settime(tfd.get(), TFD_TIMER_ABSTIME, &its, nullptr), + SyscallSucceeds()); + + absl::SleepFor(kDelay + TimerSlack()); + uint64_t val = 0; + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallSucceedsWithValue(sizeof(uint64_t))); + EXPECT_EQ(1, val); +} + +TEST(TimerfdTest, ClockRealtime) { + // Since CLOCK_REALTIME can, by definition, change, we can't make any + // non-flaky assertions about the amount of time it takes for a + // CLOCK_REALTIME-based timer to expire. Just check that it expires at all, + // and hope it happens before the test times out. + constexpr int kDelaySecs = 1; + + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_REALTIME, 0)); + struct itimerspec its = {}; + its.it_value.tv_sec = kDelaySecs; + ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), + SyscallSucceeds()); + + uint64_t val = 0; + ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), + SyscallSucceedsWithValue(sizeof(uint64_t))); + EXPECT_EQ(1, val); +} + +TEST(TimerfdTest, IllegalReadWrite) { + auto const tfd = + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); + uint64_t val = 0; + EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0), + SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0), + SyscallFailsWithErrno(ESPIPE)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/timers.cc b/test/syscalls/linux/timers.cc new file mode 100644 index 000000000..dfe231575 --- /dev/null +++ b/test/syscalls/linux/timers.cc @@ -0,0 +1,642 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/cleanup.h" +#include "test/util/logging.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_bool(timers_test_sleep, false, + "If true, sleep forever instead of running tests."); + +using ::testing::_; +using ::testing::AnyOf; + +namespace gvisor { +namespace testing { +namespace { + +#ifndef CPUCLOCK_PROF +#define CPUCLOCK_PROF 0 +#endif // CPUCLOCK_PROF + +PosixErrorOr ProcessCPUTime(pid_t pid) { + // Use pid-specific CPUCLOCK_PROF, which is the clock used to enforce + // RLIMIT_CPU. + clockid_t clockid = (~static_cast(pid) << 3) | CPUCLOCK_PROF; + + struct timespec ts; + int ret = clock_gettime(clockid, &ts); + if (ret < 0) { + return PosixError(errno, "clock_gettime failed"); + } + + return absl::DurationFromTimespec(ts); +} + +void NoopSignalHandler(int signo) { + TEST_CHECK_MSG(SIGXCPU == signo, + "NoopSigHandler did not receive expected signal"); +} + +void UninstallingSignalHandler(int signo) { + TEST_CHECK_MSG(SIGXCPU == signo, + "UninstallingSignalHandler did not receive expected signal"); + struct sigaction rev_action; + rev_action.sa_handler = SIG_DFL; + rev_action.sa_flags = 0; + sigemptyset(&rev_action.sa_mask); + sigaction(SIGXCPU, &rev_action, nullptr); +} + +TEST(TimerTest, ProcessKilledOnCPUSoftLimit) { + constexpr absl::Duration kSoftLimit = absl::Seconds(1); + constexpr absl::Duration kHardLimit = absl::Seconds(3); + + struct rlimit cpu_limits; + cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); + cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); + + int pid = fork(); + MaybeSave(); + if (pid == 0) { + TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); + MaybeSave(); + for (;;) { + } + } + auto c = Cleanup([pid] { + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(WTERMSIG(status), SIGXCPU); + }); + + // Wait for the child to exit, but do not reap it. This will allow us to check + // its CPU usage while it is zombied. + EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), + SyscallSucceeds()); + + // Assert that the child spent 1s of CPU before getting killed. + // + // We must be careful to use CPUCLOCK_PROF, the same clock used for RLIMIT_CPU + // enforcement, to get correct results. Note that this is slightly different + // from rusage-reported CPU usage: + // + // RLIMIT_CPU, CPUCLOCK_PROF use kernel/sched/cputime.c:thread_group_cputime. + // rusage uses kernel/sched/cputime.c:thread_group_cputime_adjusted. + absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); + EXPECT_GE(cpu, kSoftLimit); + + // Child did not make it to the hard limit. + // + // Linux sends SIGXCPU synchronously with CPU tick updates. See + // kernel/time/timer.c:update_process_times: + // => account_process_tick // update task CPU usage. + // => run_posix_cpu_timers // enforce RLIMIT_CPU, sending signal. + // + // Thus, only chance for this to flake is if the system time required to + // deliver the signal exceeds 2s. + EXPECT_LT(cpu, kHardLimit); +} + +TEST(TimerTest, ProcessPingedRepeatedlyAfterCPUSoftLimit) { + struct sigaction new_action; + new_action.sa_handler = UninstallingSignalHandler; + new_action.sa_flags = 0; + sigemptyset(&new_action.sa_mask); + + constexpr absl::Duration kSoftLimit = absl::Seconds(1); + constexpr absl::Duration kHardLimit = absl::Seconds(10); + + struct rlimit cpu_limits; + cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); + cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); + + int pid = fork(); + MaybeSave(); + if (pid == 0) { + TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0); + MaybeSave(); + TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); + MaybeSave(); + for (;;) { + } + } + auto c = Cleanup([pid] { + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(WTERMSIG(status), SIGXCPU); + }); + + // Wait for the child to exit, but do not reap it. This will allow us to check + // its CPU usage while it is zombied. + EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), + SyscallSucceeds()); + + absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); + // Following signals come every CPU second. + EXPECT_GE(cpu, kSoftLimit + absl::Seconds(1)); + + // Child did not make it to the hard limit. + // + // As above, should not flake. + EXPECT_LT(cpu, kHardLimit); +} + +TEST(TimerTest, ProcessKilledOnCPUHardLimit) { + struct sigaction new_action; + new_action.sa_handler = NoopSignalHandler; + new_action.sa_flags = 0; + sigemptyset(&new_action.sa_mask); + + constexpr absl::Duration kSoftLimit = absl::Seconds(1); + constexpr absl::Duration kHardLimit = absl::Seconds(3); + + struct rlimit cpu_limits; + cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); + cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); + + int pid = fork(); + MaybeSave(); + if (pid == 0) { + TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0); + MaybeSave(); + TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); + MaybeSave(); + for (;;) { + } + } + auto c = Cleanup([pid] { + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(WTERMSIG(status), SIGKILL); + }); + + // Wait for the child to exit, but do not reap it. This will allow us to check + // its CPU usage while it is zombied. + EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), + SyscallSucceeds()); + + absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); + EXPECT_GE(cpu, kHardLimit); +} + +// RAII type for a kernel "POSIX" interval timer. (The kernel provides system +// calls such as timer_create that behave very similarly, but not identically, +// to those described by timer_create(2); in particular, the kernel does not +// implement SIGEV_THREAD. glibc builds POSIX-compliant interval timers based on +// these kernel interval timers.) +// +// Compare implementation to FileDescriptor. +class IntervalTimer { + public: + IntervalTimer() = default; + + explicit IntervalTimer(int id) { set_id(id); } + + IntervalTimer(IntervalTimer&& orig) : id_(orig.release()) {} + + IntervalTimer& operator=(IntervalTimer&& orig) { + if (this == &orig) return *this; + reset(orig.release()); + return *this; + } + + IntervalTimer(const IntervalTimer& other) = delete; + IntervalTimer& operator=(const IntervalTimer& other) = delete; + + ~IntervalTimer() { reset(); } + + int get() const { return id_; } + + int release() { + int const id = id_; + id_ = -1; + return id; + } + + void reset() { reset(-1); } + + void reset(int id) { + if (id_ >= 0) { + TEST_PCHECK(syscall(SYS_timer_delete, id_) == 0); + MaybeSave(); + } + set_id(id); + } + + PosixErrorOr Set( + int flags, const struct itimerspec& new_value) const { + struct itimerspec old_value = {}; + if (syscall(SYS_timer_settime, id_, flags, &new_value, &old_value) < 0) { + return PosixError(errno, "timer_settime"); + } + MaybeSave(); + return old_value; + } + + PosixErrorOr Get() const { + struct itimerspec curr_value = {}; + if (syscall(SYS_timer_gettime, id_, &curr_value) < 0) { + return PosixError(errno, "timer_gettime"); + } + MaybeSave(); + return curr_value; + } + + PosixErrorOr Overruns() const { + int rv = syscall(SYS_timer_getoverrun, id_); + if (rv < 0) { + return PosixError(errno, "timer_getoverrun"); + } + MaybeSave(); + return rv; + } + + private: + void set_id(int id) { id_ = std::max(id, -1); } + + // Kernel timer_t is int; glibc timer_t is void*. + int id_ = -1; +}; + +PosixErrorOr TimerCreate(clockid_t clockid, + const struct sigevent& sev) { + int timerid; + if (syscall(SYS_timer_create, clockid, &sev, &timerid) < 0) { + return PosixError(errno, "timer_create"); + } + MaybeSave(); + return IntervalTimer(timerid); +} + +// See timerfd.cc:TimerSlack() for rationale. +constexpr absl::Duration kTimerSlack = absl::Milliseconds(500); + +TEST(IntervalTimerTest, IsInitiallyStopped) { + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_NONE; + const auto timer = + ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + const struct itimerspec its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); + EXPECT_EQ(0, its.it_value.tv_sec); + EXPECT_EQ(0, its.it_value.tv_nsec); +} + +TEST(IntervalTimerTest, SingleShotSilent) { + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_NONE; + const auto timer = + ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + + constexpr absl::Duration kDelay = absl::Seconds(1); + struct itimerspec its = {}; + its.it_value = absl::ToTimespec(kDelay); + ASSERT_NO_ERRNO(timer.Set(0, its)); + + // The timer should count down to 0 and stop since the interval is zero. No + // overruns should be counted. + absl::SleepFor(kDelay + kTimerSlack); + its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); + EXPECT_EQ(0, its.it_value.tv_sec); + EXPECT_EQ(0, its.it_value.tv_nsec); + EXPECT_THAT(timer.Overruns(), IsPosixErrorOkAndHolds(0)); +} + +TEST(IntervalTimerTest, PeriodicSilent) { + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_NONE; + const auto timer = + ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + + constexpr absl::Duration kPeriod = absl::Seconds(1); + struct itimerspec its = {}; + its.it_value = its.it_interval = absl::ToTimespec(kPeriod); + ASSERT_NO_ERRNO(timer.Set(0, its)); + + absl::SleepFor(kPeriod * 3 + kTimerSlack); + + // The timer should still be running. + its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); + EXPECT_TRUE(its.it_value.tv_nsec != 0 || its.it_value.tv_sec != 0); + + // Timer expirations are not counted as overruns under SIGEV_NONE. + EXPECT_THAT(timer.Overruns(), IsPosixErrorOkAndHolds(0)); +} + +std::atomic counted_signals; + +void IntervalTimerCountingSignalHandler(int sig, siginfo_t* info, + void* ucontext) { + counted_signals.fetch_add(1 + info->si_overrun); +} + +TEST(IntervalTimerTest, PeriodicGroupDirectedSignal) { + constexpr int kSigno = SIGUSR1; + constexpr int kSigvalue = 42; + + // Install our signal handler. + counted_signals.store(0); + struct sigaction sa = {}; + sa.sa_sigaction = IntervalTimerCountingSignalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + const auto scoped_sigaction = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); + + // Ensure that kSigno is unblocked on at least one thread. + const auto scoped_sigmask = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, kSigno)); + + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_signo = kSigno; + sev.sigev_value.sival_int = kSigvalue; + auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + + constexpr absl::Duration kPeriod = absl::Seconds(1); + constexpr int kCycles = 3; + struct itimerspec its = {}; + its.it_value = its.it_interval = absl::ToTimespec(kPeriod); + ASSERT_NO_ERRNO(timer.Set(0, its)); + + absl::SleepFor(kPeriod * kCycles + kTimerSlack); + EXPECT_GE(counted_signals.load(), kCycles); +} + +// From Linux's include/uapi/asm-generic/siginfo.h. +#ifndef sigev_notify_thread_id +#define sigev_notify_thread_id _sigev_un._tid +#endif + +TEST(IntervalTimerTest, PeriodicThreadDirectedSignal) { + constexpr int kSigno = SIGUSR1; + constexpr int kSigvalue = 42; + + // Block kSigno so that we can accumulate overruns. + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, kSigno); + const auto scoped_sigmask = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); + + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_THREAD_ID; + sev.sigev_signo = kSigno; + sev.sigev_value.sival_int = kSigvalue; + sev.sigev_notify_thread_id = gettid(); + auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + + constexpr absl::Duration kPeriod = absl::Seconds(1); + constexpr int kCycles = 3; + struct itimerspec its = {}; + its.it_value = its.it_interval = absl::ToTimespec(kPeriod); + ASSERT_NO_ERRNO(timer.Set(0, its)); + absl::SleepFor(kPeriod * kCycles + kTimerSlack); + + // At least kCycles expirations should have occurred, resulting in kCycles-1 + // overruns (the first expiration sent the signal successfully). + siginfo_t si; + struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); + ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), + SyscallSucceedsWithValue(kSigno)); + EXPECT_EQ(si.si_signo, kSigno); + EXPECT_EQ(si.si_code, SI_TIMER); + EXPECT_EQ(si.si_timerid, timer.get()); + EXPECT_GE(si.si_overrun, kCycles - 1); + EXPECT_EQ(si.si_int, kSigvalue); + + // Kill the timer, then drain any additional signal it may have enqueued. We + // can't do this before the preceding sigtimedwait because stopping or + // deleting the timer resets si_overrun to 0. + timer.reset(); + sigtimedwait(&mask, &si, &zero_ts); +} + +TEST(IntervalTimerTest, OtherThreadGroup) { + constexpr int kSigno = SIGUSR1; + + // Create a subprocess that does nothing until killed. + pid_t child_pid; + const auto sp = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( + "/proc/self/exe", ExecveArray({"timers", "--timers_test_sleep"}), + ExecveArray(), &child_pid, nullptr)); + + // Verify that we can't create a timer that would send signals to it. + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_THREAD_ID; + sev.sigev_signo = kSigno; + sev.sigev_notify_thread_id = child_pid; + EXPECT_THAT(TimerCreate(CLOCK_MONOTONIC, sev), PosixErrorIs(EINVAL, _)); +} + +TEST(IntervalTimerTest, RealTimeSignalsAreNotDuplicated) { + const int kSigno = SIGRTMIN; + constexpr int kSigvalue = 42; + + // Block signo so that we can accumulate overruns. + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, kSigno); + const auto scoped_sigmask = ScopedSignalMask(SIG_BLOCK, mask); + + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_THREAD_ID; + sev.sigev_signo = kSigno; + sev.sigev_value.sival_int = kSigvalue; + sev.sigev_notify_thread_id = gettid(); + const auto timer = + ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + + constexpr absl::Duration kPeriod = absl::Seconds(1); + constexpr int kCycles = 3; + struct itimerspec its = {}; + its.it_value = its.it_interval = absl::ToTimespec(kPeriod); + ASSERT_NO_ERRNO(timer.Set(0, its)); + absl::SleepFor(kPeriod * kCycles + kTimerSlack); + + // Stop the timer so that no further signals are enqueued after sigtimedwait. + struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); + its.it_value = its.it_interval = zero_ts; + ASSERT_NO_ERRNO(timer.Set(0, its)); + + // The timer should have sent only a single signal, even though the kernel + // supports enqueueing of multiple RT signals. + siginfo_t si; + ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), + SyscallSucceedsWithValue(kSigno)); + EXPECT_EQ(si.si_signo, kSigno); + EXPECT_EQ(si.si_code, SI_TIMER); + EXPECT_EQ(si.si_timerid, timer.get()); + // si_overrun was reset by timer_settime. + EXPECT_EQ(si.si_overrun, 0); + EXPECT_EQ(si.si_int, kSigvalue); + EXPECT_THAT(sigtimedwait(&mask, &si, &zero_ts), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST(IntervalTimerTest, AlreadyPendingSignal) { + constexpr int kSigno = SIGUSR1; + constexpr int kSigvalue = 42; + + // Block kSigno so that we can accumulate overruns. + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, kSigno); + const auto scoped_sigmask = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); + + // Send ourselves a signal, preventing the timer from enqueuing. + ASSERT_THAT(tgkill(getpid(), gettid(), kSigno), SyscallSucceeds()); + + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_THREAD_ID; + sev.sigev_signo = kSigno; + sev.sigev_value.sival_int = kSigvalue; + sev.sigev_notify_thread_id = gettid(); + auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + + constexpr absl::Duration kPeriod = absl::Seconds(1); + constexpr int kCycles = 3; + struct itimerspec its = {}; + its.it_value = its.it_interval = absl::ToTimespec(kPeriod); + ASSERT_NO_ERRNO(timer.Set(0, its)); + + // End the sleep one cycle short; we will sleep for one more cycle below. + absl::SleepFor(kPeriod * (kCycles - 1)); + + // Dequeue the first signal, which we sent to ourselves with tgkill. + siginfo_t si; + struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); + ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), + SyscallSucceedsWithValue(kSigno)); + EXPECT_EQ(si.si_signo, kSigno); + // glibc sigtimedwait silently replaces SI_TKILL with SI_USER: + // sysdeps/unix/sysv/linux/sigtimedwait.c:__sigtimedwait(). This isn't + // documented, so we don't depend on it. + EXPECT_THAT(si.si_code, AnyOf(SI_USER, SI_TKILL)); + + // Sleep for 1 more cycle to give the timer time to send a signal. + absl::SleepFor(kPeriod + kTimerSlack); + + // At least kCycles expirations should have occurred, resulting in kCycles-1 + // overruns (the last expiration sent the signal successfully). + ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), + SyscallSucceedsWithValue(kSigno)); + EXPECT_EQ(si.si_signo, kSigno); + EXPECT_EQ(si.si_code, SI_TIMER); + EXPECT_EQ(si.si_timerid, timer.get()); + EXPECT_GE(si.si_overrun, kCycles - 1); + EXPECT_EQ(si.si_int, kSigvalue); + + // Kill the timer, then drain any additional signal it may have enqueued. We + // can't do this before the preceding sigtimedwait because stopping or + // deleting the timer resets si_overrun to 0. + timer.reset(); + sigtimedwait(&mask, &si, &zero_ts); +} + +TEST(IntervalTimerTest, IgnoredSignalCountsAsOverrun) { + constexpr int kSigno = SIGUSR1; + constexpr int kSigvalue = 42; + + // Ignore kSigno. + struct sigaction sa = {}; + sa.sa_handler = SIG_IGN; + const auto scoped_sigaction = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); + + // Unblock kSigno so that ignored signals will be discarded. + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, kSigno); + auto scoped_sigmask = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, mask)); + + struct sigevent sev = {}; + sev.sigev_notify = SIGEV_THREAD_ID; + sev.sigev_signo = kSigno; + sev.sigev_value.sival_int = kSigvalue; + sev.sigev_notify_thread_id = gettid(); + auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); + + constexpr absl::Duration kPeriod = absl::Seconds(1); + constexpr int kCycles = 3; + struct itimerspec its = {}; + its.it_value = its.it_interval = absl::ToTimespec(kPeriod); + ASSERT_NO_ERRNO(timer.Set(0, its)); + + // End the sleep one cycle short; we will sleep for one more cycle below. + absl::SleepFor(kPeriod * (kCycles - 1)); + + // Block kSigno so that ignored signals will be enqueued. + scoped_sigmask.Release()(); + scoped_sigmask = ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); + + // Sleep for 1 more cycle to give the timer time to send a signal. + absl::SleepFor(kPeriod + kTimerSlack); + + // At least kCycles expirations should have occurred, resulting in kCycles-1 + // overruns (the last expiration sent the signal successfully). + siginfo_t si; + struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); + ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), + SyscallSucceedsWithValue(kSigno)); + EXPECT_EQ(si.si_signo, kSigno); + EXPECT_EQ(si.si_code, SI_TIMER); + EXPECT_EQ(si.si_timerid, timer.get()); + EXPECT_GE(si.si_overrun, kCycles - 1); + EXPECT_EQ(si.si_int, kSigvalue); + + // Kill the timer, then drain any additional signal it may have enqueued. We + // can't do this before the preceding sigtimedwait because stopping or + // deleting the timer resets si_overrun to 0. + timer.reset(); + sigtimedwait(&mask, &si, &zero_ts); +} + +} // namespace +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + gvisor::testing::TestInit(&argc, &argv); + + if (FLAGS_timers_test_sleep) { + while (true) { + absl::SleepFor(absl::Seconds(10)); + } + } + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/tkill.cc b/test/syscalls/linux/tkill.cc new file mode 100644 index 000000000..9842ccc9b --- /dev/null +++ b/test/syscalls/linux/tkill.cc @@ -0,0 +1,75 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/logging.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +static int tkill(pid_t tid, int sig) { + int ret; + do { + // NOTE: tkill(2) could return EAGAIN for RT signals. + ret = syscall(SYS_tkill, tid, sig); + } while (ret == -1 && errno == EAGAIN); + return ret; +} + +TEST(TkillTest, InvalidTID) { + EXPECT_THAT(tkill(-1, 0), SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(tkill(0, 0), SyscallFailsWithErrno(EINVAL)); +} + +TEST(TkillTest, ValidTID) { + EXPECT_THAT(tkill(gettid(), 0), SyscallSucceeds()); +} + +void SigHandler(int sig, siginfo_t* info, void* context) { + TEST_CHECK(sig == SIGRTMAX); + TEST_CHECK(info->si_pid == getpid()); + TEST_CHECK(info->si_uid == getuid()); + TEST_CHECK(info->si_code == SI_TKILL); +} + +// Test with a real signal. +TEST(TkillTest, ValidTIDAndRealSignal) { + struct sigaction sa; + sa.sa_sigaction = SigHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + ASSERT_THAT(sigaction(SIGRTMAX, &sa, nullptr), SyscallSucceeds()); + // InitGoogle blocks all RT signals, so we need undo it. + sigset_t unblock; + sigemptyset(&unblock); + sigaddset(&unblock, SIGRTMAX); + ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &unblock, nullptr), SyscallSucceeds()); + EXPECT_THAT(tkill(gettid(), SIGRTMAX), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/truncate.cc b/test/syscalls/linux/truncate.cc new file mode 100644 index 000000000..2616a9147 --- /dev/null +++ b/test/syscalls/linux/truncate.cc @@ -0,0 +1,217 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/syscalls/linux/file_base.h" +#include "test/util/capability_util.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class FixtureTruncateTest : public FileTest { + void SetUp() override { FileTest::SetUp(); } +}; + +TEST_F(FixtureTruncateTest, Truncate) { + // Get the current rlimit and restore after test run. + struct rlimit initial_lim; + ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + auto cleanup = Cleanup([&initial_lim] { + EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + }); + + // Check that it starts at size zero. + struct stat buf; + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 0); + + // Stay at size zero. + EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds()); + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 0); + + // Grow to ten bytes. + EXPECT_THAT(truncate(test_file_name_.c_str(), 10), SyscallSucceeds()); + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 10); + + // Can't be truncated to a negative number. + EXPECT_THAT(truncate(test_file_name_.c_str(), -1), + SyscallFailsWithErrno(EINVAL)); + + // Try growing past the file size limit. + sigset_t new_mask; + sigemptyset(&new_mask); + sigaddset(&new_mask, SIGXFSZ); + sigprocmask(SIG_BLOCK, &new_mask, nullptr); + struct timespec timelimit; + timelimit.tv_sec = 10; + timelimit.tv_nsec = 0; + + struct rlimit setlim; + setlim.rlim_cur = 1024; + setlim.rlim_max = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); + EXPECT_THAT(truncate(test_file_name_.c_str(), 1025), + SyscallFailsWithErrno(EFBIG)); + EXPECT_EQ(sigtimedwait(&new_mask, nullptr, &timelimit), SIGXFSZ); + ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &new_mask, nullptr), SyscallSucceeds()); + + // Shrink back down to zero. + EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds()); + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 0); +} + +TEST_F(FixtureTruncateTest, Ftruncate) { + // Get the current rlimit and restore after test run. + struct rlimit initial_lim; + ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + auto cleanup = Cleanup([&initial_lim] { + EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + }); + + // Check that it starts at size zero. + struct stat buf; + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 0); + + // Stay at size zero. + EXPECT_THAT(ftruncate(test_file_fd_.get(), 0), SyscallSucceeds()); + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 0); + + // Grow to ten bytes. + EXPECT_THAT(ftruncate(test_file_fd_.get(), 10), SyscallSucceeds()); + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 10); + + // Can't be truncated to a negative number. + EXPECT_THAT(ftruncate(test_file_fd_.get(), -1), + SyscallFailsWithErrno(EINVAL)); + + // Try growing past the file size limit. + sigset_t new_mask; + sigemptyset(&new_mask); + sigaddset(&new_mask, SIGXFSZ); + sigprocmask(SIG_BLOCK, &new_mask, nullptr); + struct timespec timelimit; + timelimit.tv_sec = 10; + timelimit.tv_nsec = 0; + + struct rlimit setlim; + setlim.rlim_cur = 1024; + setlim.rlim_max = RLIM_INFINITY; + ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); + EXPECT_THAT(ftruncate(test_file_fd_.get(), 1025), + SyscallFailsWithErrno(EFBIG)); + EXPECT_EQ(sigtimedwait(&new_mask, nullptr, &timelimit), SIGXFSZ); + ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &new_mask, nullptr), SyscallSucceeds()); + + // Shrink back down to zero. + EXPECT_THAT(ftruncate(test_file_fd_.get(), 0), SyscallSucceeds()); + ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); + EXPECT_EQ(buf.st_size, 0); +} + +// Truncating a file down clears that portion of the file. +TEST_F(FixtureTruncateTest, FtruncateShrinkGrow) { + std::vector buf(10, 'a'); + EXPECT_THAT(WriteFd(test_file_fd_.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + // Shrink then regrow the file. This should clear the second half of the file. + EXPECT_THAT(ftruncate(test_file_fd_.get(), 5), SyscallSucceeds()); + EXPECT_THAT(ftruncate(test_file_fd_.get(), 10), SyscallSucceeds()); + + EXPECT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), SyscallSucceeds()); + + std::vector buf2(10); + EXPECT_THAT(ReadFd(test_file_fd_.get(), buf2.data(), buf2.size()), + SyscallSucceedsWithValue(buf2.size())); + + std::vector expect = {'a', 'a', 'a', 'a', 'a', + '\0', '\0', '\0', '\0', '\0'}; + EXPECT_EQ(expect, buf2); +} + +TEST(TruncateTest, TruncateDir) { + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + EXPECT_THAT(truncate(temp_dir.path().c_str(), 0), + SyscallFailsWithErrno(EISDIR)); +} + +TEST(TruncateTest, FtruncateDir) { + auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(temp_dir.path(), O_DIRECTORY | O_RDONLY)); + EXPECT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL)); +} + +TEST(TruncateTest, TruncateNonWriteable) { + // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to + // always override write permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */)); + EXPECT_THAT(truncate(temp_file.path().c_str(), 0), + SyscallFailsWithErrno(EACCES)); +} + +TEST(TruncateTest, FtruncateNonWriteable) { + auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY)); + EXPECT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL)); +} + +TEST(TruncateTest, TruncateNonExist) { + EXPECT_THAT(truncate("/foo/bar", 0), SyscallFailsWithErrno(ENOENT)); +} + +TEST(TruncateTest, FtruncateVirtualTmp_NoRandomSave) { + auto temp_file = NewTempAbsPathInDir("/dev/shm"); + const DisableSave ds; // Incompatible permissions. + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file, O_RDWR | O_CREAT | O_EXCL, 0)); + EXPECT_THAT(ftruncate(fd.get(), 100), SyscallSucceeds()); +} + +// NOTE: There are additional truncate(2)/ftruncate(2) tests in mknod.cc +// which are there to avoid running the tests on a number of different +// filesystems which may not support mknod. + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/udp_bind.cc b/test/syscalls/linux/udp_bind.cc new file mode 100644 index 000000000..419aaac76 --- /dev/null +++ b/test/syscalls/linux/udp_bind.cc @@ -0,0 +1,316 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +struct sockaddr_in_common { + sa_family_t sin_family; + in_port_t sin_port; +}; + +struct SendtoTestParam { + // Human readable description of test parameter. + std::string description; + + // Test is broken in gVisor, skip. + bool skip_on_gvisor; + + // Domain for the socket that will do the sending. + int send_domain; + + // Address to bind for the socket that will do the sending. + struct sockaddr_storage send_addr; + socklen_t send_addr_len; // 0 for unbound. + + // Address to connect to for the socket that will do the sending. + struct sockaddr_storage connect_addr; + socklen_t connect_addr_len; // 0 for no connection. + + // Domain for the socket that will do the receiving. + int recv_domain; + + // Address to bind for the socket that will do the receiving. + struct sockaddr_storage recv_addr; + socklen_t recv_addr_len; + + // Address to send to. + struct sockaddr_storage sendto_addr; + socklen_t sendto_addr_len; + + // Expected errno for the sendto call. + std::vector sendto_errnos; // empty on success. +}; + +class SendtoTest : public ::testing::TestWithParam { + protected: + SendtoTest() { + // gUnit uses printf, so so will we. + printf("Testing with %s\n", GetParam().description.c_str()); + } +}; + +TEST_P(SendtoTest, Sendto) { + auto param = GetParam(); + + SKIP_IF(param.skip_on_gvisor && IsRunningOnGvisor()); + + const FileDescriptor s1 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(param.send_domain, SOCK_DGRAM, 0)); + const FileDescriptor s2 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(param.recv_domain, SOCK_DGRAM, 0)); + + if (param.send_addr_len > 0) { + ASSERT_THAT(bind(s1.get(), reinterpret_cast(¶m.send_addr), + param.send_addr_len), + SyscallSucceeds()); + } + + if (param.connect_addr_len > 0) { + ASSERT_THAT( + connect(s1.get(), reinterpret_cast(¶m.connect_addr), + param.connect_addr_len), + SyscallSucceeds()); + } + + ASSERT_THAT(bind(s2.get(), reinterpret_cast(¶m.recv_addr), + param.recv_addr_len), + SyscallSucceeds()); + + struct sockaddr_storage real_recv_addr = {}; + socklen_t real_recv_addr_len = param.recv_addr_len; + ASSERT_THAT( + getsockname(s2.get(), reinterpret_cast(&real_recv_addr), + &real_recv_addr_len), + SyscallSucceeds()); + + ASSERT_EQ(real_recv_addr_len, param.recv_addr_len); + + int recv_port = + reinterpret_cast(&real_recv_addr)->sin_port; + + struct sockaddr_storage sendto_addr = param.sendto_addr; + reinterpret_cast(&sendto_addr)->sin_port = recv_port; + + char buf[20] = {}; + if (!param.sendto_errnos.empty()) { + ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0, + reinterpret_cast(&sendto_addr), + param.sendto_addr_len), + SyscallFailsWithErrno(ElementOf(param.sendto_errnos))); + return; + } + + ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0, + reinterpret_cast(&sendto_addr), + param.sendto_addr_len), + SyscallSucceedsWithValue(sizeof(buf))); + + struct sockaddr_storage got_addr = {}; + socklen_t got_addr_len = sizeof(sockaddr_storage); + ASSERT_THAT(RetryEINTR(recvfrom)(s2.get(), buf, sizeof(buf), 0, + reinterpret_cast(&got_addr), + &got_addr_len), + SyscallSucceedsWithValue(sizeof(buf))); + + ASSERT_GT(got_addr_len, sizeof(sockaddr_in_common)); + int got_port = reinterpret_cast(&got_addr)->sin_port; + + struct sockaddr_storage sender_addr = {}; + socklen_t sender_addr_len = sizeof(sockaddr_storage); + ASSERT_THAT(getsockname(s1.get(), reinterpret_cast(&sender_addr), + &sender_addr_len), + SyscallSucceeds()); + + ASSERT_GT(sender_addr_len, sizeof(sockaddr_in_common)); + int sender_port = + reinterpret_cast(&sender_addr)->sin_port; + + EXPECT_EQ(got_port, sender_port); +} + +socklen_t Ipv4Addr(sockaddr_storage* addr, int port = 0) { + auto addr4 = reinterpret_cast(addr); + addr4->sin_family = AF_INET; + addr4->sin_port = port; + inet_pton(AF_INET, "127.0.0.1", &addr4->sin_addr.s_addr); + return sizeof(struct sockaddr_in); +} + +socklen_t Ipv6Addr(sockaddr_storage* addr, int port = 0) { + auto addr6 = reinterpret_cast(addr); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = port; + inet_pton(AF_INET6, "::1", &addr6->sin6_addr.s6_addr); + return sizeof(struct sockaddr_in6); +} + +socklen_t Ipv4MappedIpv6Addr(sockaddr_storage* addr, int port = 0) { + auto addr6 = reinterpret_cast(addr); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = port; + inet_pton(AF_INET6, "::ffff:127.0.0.1", &addr6->sin6_addr.s6_addr); + return sizeof(struct sockaddr_in6); +} + +INSTANTIATE_TEST_CASE_P( + UdpBindTest, SendtoTest, + ::testing::Values( + []() { + SendtoTestParam param = {}; + param.description = "IPv4 mapped IPv6 sendto IPv4 mapped IPv6"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv6 sendto IPv6"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv6Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 sendto IPv4"; + param.send_domain = AF_INET; + param.send_addr_len = Ipv4Addr(¶m.send_addr); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 mapped IPv6 sendto IPv4"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 sendto IPv4 mapped IPv6"; + param.send_domain = AF_INET; + param.send_addr_len = Ipv4Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "unbound IPv6 sendto IPv4 mapped IPv6"; + param.send_domain = AF_INET6; + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "unbound IPv6 sendto IPv4"; + param.send_domain = AF_INET6; + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv6 sendto IPv4"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv6Addr(¶m.send_addr); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + param.sendto_errnos = {ENETUNREACH}; + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 mapped IPv6 sendto IPv6"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); + param.sendto_errnos = {EAFNOSUPPORT}; + // The errno returned changed in Linux commit c8e6ad0829a723. + param.sendto_errnos = {EINVAL, EAFNOSUPPORT}; + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "connected IPv4 mapped IPv6 sendto IPv6"; + param.send_domain = AF_INET6; + param.connect_addr_len = + Ipv4MappedIpv6Addr(¶m.connect_addr, 5000); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); + // The errno returned changed in Linux commit c8e6ad0829a723. + param.sendto_errnos = {EINVAL, EAFNOSUPPORT}; + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "connected IPv6 sendto IPv4 mapped IPv6"; + // TODO: Determine if this inconsistent behavior is worth + // implementing. + param.skip_on_gvisor = true; + param.send_domain = AF_INET6; + param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "connected IPv6 sendto IPv4"; + // TODO: Determine if this inconsistent behavior is worth + // implementing. + param.skip_on_gvisor = true; + param.send_domain = AF_INET6; + param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }())); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc new file mode 100644 index 000000000..a02b418a3 --- /dev/null +++ b/test/syscalls/linux/udp_socket.cc @@ -0,0 +1,941 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/macros.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// The initial port to be be used on gvisor. +constexpr int TestPort = 40000; + +// Fixture for tests parameterized by the address family to use (AF_INET and +// AF_INET6) when creating sockets. +class UdpSocketTest : public ::testing::TestWithParam { + protected: + // Creates two sockets that will be used by test cases. + void SetUp() override; + + // Closes the sockets created by SetUp(). + void TearDown() override { + EXPECT_THAT(close(s_), SyscallSucceeds()); + EXPECT_THAT(close(t_), SyscallSucceeds()); + + for (size_t i = 0; i < ABSL_ARRAYSIZE(ports_); ++i) { + ASSERT_NO_ERRNO(FreeAvailablePort(ports_[i])); + } + } + + // First UDP socket. + int s_; + + // Second UDP socket. + int t_; + + // The length of the socket address. + socklen_t addrlen_; + + // Initialized address pointing to loopback and port TestPort+i. + struct sockaddr* addr_[3]; + + // Initialize "any" address. + struct sockaddr* anyaddr_; + + // Used ports. + int ports_[3]; + + private: + // Storage for the loopback addresses. + struct sockaddr_storage addr_storage_[3]; + + // Storage for the "any" address. + struct sockaddr_storage anyaddr_storage_; +}; + +// Gets a pointer to the port component of the given address. +uint16_t* Port(struct sockaddr_storage* addr) { + switch (addr->ss_family) { + case AF_INET: { + auto sin = reinterpret_cast(addr); + return &sin->sin_port; + } + case AF_INET6: { + auto sin6 = reinterpret_cast(addr); + return &sin6->sin6_port; + } + } + + return nullptr; +} + +void UdpSocketTest::SetUp() { + ASSERT_THAT(s_ = socket(GetParam(), SOCK_DGRAM, IPPROTO_UDP), + SyscallSucceeds()); + + ASSERT_THAT(t_ = socket(GetParam(), SOCK_DGRAM, IPPROTO_UDP), + SyscallSucceeds()); + + memset(&anyaddr_storage_, 0, sizeof(anyaddr_storage_)); + anyaddr_ = reinterpret_cast(&anyaddr_storage_); + anyaddr_->sa_family = GetParam(); + + // Initialize address-family-specific values. + switch (GetParam()) { + case AF_INET: { + auto sin = reinterpret_cast(&anyaddr_storage_); + addrlen_ = sizeof(*sin); + sin->sin_addr.s_addr = htonl(INADDR_ANY); + break; + } + case AF_INET6: { + auto sin6 = reinterpret_cast(&anyaddr_storage_); + addrlen_ = sizeof(*sin6); + sin6->sin6_addr = in6addr_any; + break; + } + } + + if (gvisor::testing::IsRunningOnGvisor()) { + for (size_t i = 0; i < ABSL_ARRAYSIZE(ports_); ++i) { + ports_[i] = TestPort + i; + } + } else { + // When not under gvisor, use utility function to pick port. Assert that + // all ports are different. + std::string error; + for (size_t i = 0; i < ABSL_ARRAYSIZE(ports_); ++i) { + // Find an unused port, we specify port 0 to allow the kernel to provide + // the port. + bool unique = true; + do { + ports_[i] = ASSERT_NO_ERRNO_AND_VALUE(PortAvailable( + 0, AddressFamily::kDualStack, SocketType::kUdp, false)); + ASSERT_GT(ports_[i], 0); + for (size_t j = 0; j < i; ++j) { + if (ports_[j] == ports_[i]) { + unique = false; + break; + } + } + } while (!unique); + } + } + + // Initialize the sockaddrs. + for (size_t i = 0; i < ABSL_ARRAYSIZE(addr_); ++i) { + memset(&addr_storage_[i], 0, sizeof(addr_storage_[i])); + + addr_[i] = reinterpret_cast(&addr_storage_[i]); + addr_[i]->sa_family = GetParam(); + + switch (GetParam()) { + case AF_INET: { + auto sin = reinterpret_cast(addr_[i]); + sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin->sin_port = htons(ports_[i]); + break; + } + case AF_INET6: { + auto sin6 = reinterpret_cast(addr_[i]); + sin6->sin6_addr = in6addr_loopback; + sin6->sin6_port = htons(ports_[i]); + break; + } + } + } +} + +TEST_P(UdpSocketTest, Creation) { + int s_; + + ASSERT_THAT(s_ = socket(GetParam(), SOCK_DGRAM, IPPROTO_UDP), + SyscallSucceeds()); + EXPECT_THAT(close(s_), SyscallSucceeds()); + + ASSERT_THAT(s_ = socket(GetParam(), SOCK_DGRAM, 0), SyscallSucceeds()); + EXPECT_THAT(close(s_), SyscallSucceeds()); + + ASSERT_THAT(s_ = socket(GetParam(), SOCK_STREAM, IPPROTO_UDP), + SyscallFails()); +} + +TEST_P(UdpSocketTest, Getsockname) { + // Check that we're not bound. + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getsockname(s_, reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, addrlen_); + EXPECT_EQ(memcmp(&addr, anyaddr_, addrlen_), 0); + + // Bind, then check that we get the right address. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + addrlen = sizeof(addr); + EXPECT_THAT(getsockname(s_, reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, addrlen_); + EXPECT_EQ(memcmp(&addr, addr_[0], addrlen_), 0); +} + +TEST_P(UdpSocketTest, Getpeername) { + // Check that we're not connected. + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getpeername(s_, reinterpret_cast(&addr), &addrlen), + SyscallFailsWithErrno(ENOTCONN)); + + // Connect, then check that we get the right address. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + addrlen = sizeof(addr); + EXPECT_THAT(getpeername(s_, reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, addrlen_); + EXPECT_EQ(memcmp(&addr, addr_[0], addrlen_), 0); +} + +TEST_P(UdpSocketTest, SendNotConnected) { + // Do send & write, they must fail. + char buf[512]; + EXPECT_THAT(send(s_, buf, sizeof(buf), 0), + SyscallFailsWithErrno(EDESTADDRREQ)); + + EXPECT_THAT(write(s_, buf, sizeof(buf)), SyscallFailsWithErrno(EDESTADDRREQ)); + + // Use sendto. + ASSERT_THAT(sendto(s_, buf, sizeof(buf), 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + + // Check that we're bound now. + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getsockname(s_, reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, addrlen_); + EXPECT_NE(*Port(&addr), 0); +} + +TEST_P(UdpSocketTest, ConnectBinds) { + // Connect the socket. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Check that we're bound now. + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getsockname(s_, reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, addrlen_); + EXPECT_NE(*Port(&addr), 0); +} + +TEST_P(UdpSocketTest, ReceiveNotBound) { + char buf[512]; + EXPECT_THAT(recv(s_, buf, sizeof(buf), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); +} + +TEST_P(UdpSocketTest, Bind) { + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Try to bind again. + EXPECT_THAT(bind(s_, addr_[1], addrlen_), SyscallFailsWithErrno(EINVAL)); + + // Check that we're still bound to the original address. + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getsockname(s_, reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, addrlen_); + EXPECT_EQ(memcmp(&addr, addr_[0], addrlen_), 0); +} + +TEST_P(UdpSocketTest, BindInUse) { + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Try to bind again. + EXPECT_THAT(bind(t_, addr_[0], addrlen_), SyscallFailsWithErrno(EADDRINUSE)); +} + +TEST_P(UdpSocketTest, ReceiveAfterConnect) { + // Connect s_ to loopback:TestPort, and bind t_ to loopback:TestPort. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + ASSERT_THAT(bind(t_, addr_[0], addrlen_), SyscallSucceeds()); + + // Get the address s_ was bound to during connect. + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(getsockname(s_, reinterpret_cast(&addr), &addrlen), + SyscallSucceeds()); + EXPECT_EQ(addrlen, addrlen_); + + // Send from t_ to s_. + char buf[512]; + RandomizeBuffer(buf, sizeof(buf)); + ASSERT_THAT(sendto(t_, buf, sizeof(buf), 0, + reinterpret_cast(&addr), addrlen), + SyscallSucceedsWithValue(sizeof(buf))); + + // Receive the data. + char received[512]; + EXPECT_THAT(recv(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(sizeof(received))); + EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); +} + +TEST_P(UdpSocketTest, Connect) { + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Check that we're connected to the right peer. + struct sockaddr_storage peer; + socklen_t peerlen = sizeof(peer); + EXPECT_THAT(getpeername(s_, reinterpret_cast(&peer), &peerlen), + SyscallSucceeds()); + EXPECT_EQ(peerlen, addrlen_); + EXPECT_EQ(memcmp(&peer, addr_[0], addrlen_), 0); + + // Try to bind after connect. + EXPECT_THAT(bind(s_, addr_[1], addrlen_), SyscallFailsWithErrno(EINVAL)); + + // Try to connect again. + EXPECT_THAT(connect(s_, addr_[2], addrlen_), SyscallSucceeds()); + + // Check that peer name changed. + peerlen = sizeof(peer); + EXPECT_THAT(getpeername(s_, reinterpret_cast(&peer), &peerlen), + SyscallSucceeds()); + EXPECT_EQ(peerlen, addrlen_); + EXPECT_EQ(memcmp(&peer, addr_[2], addrlen_), 0); +} + +TEST_P(UdpSocketTest, SendToAddressOtherThanConnected) { + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Send to a different destination than we're connected to. + char buf[512]; + EXPECT_THAT(sendto(s_, buf, sizeof(buf), 0, addr_[1], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); +} + +TEST_P(UdpSocketTest, ZerolengthWriteAllowed) { + // Bind s_ to loopback:TestPort, and connect to loopback:TestPort+1. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(s_, addr_[1], addrlen_), SyscallSucceeds()); + + // Bind t_ to loopback:TestPort+1. + ASSERT_THAT(bind(t_, addr_[1], addrlen_), SyscallSucceeds()); + + char buf[3]; + // Send zero length packet from s_ to t_. + ASSERT_THAT(write(s_, buf, 0), SyscallSucceedsWithValue(0)); + // Receive the packet. + char received[3]; + EXPECT_THAT(read(t_, received, sizeof(received)), + SyscallSucceedsWithValue(0)); +} + +TEST_P(UdpSocketTest, ZerolengthWriteAllowedNonBlockRead) { + // Bind s_ to loopback:TestPort, and connect to loopback:TestPort+1. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(s_, addr_[1], addrlen_), SyscallSucceeds()); + + // Bind t_ to loopback:TestPort+1. + ASSERT_THAT(bind(t_, addr_[1], addrlen_), SyscallSucceeds()); + + // Set t_ to non-blocking. + int opts = 0; + ASSERT_THAT(opts = fcntl(t_, F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(t_, F_SETFL, opts | O_NONBLOCK), SyscallSucceeds()); + + char buf[3]; + // Send zero length packet from s_ to t_. + ASSERT_THAT(write(s_, buf, 0), SyscallSucceedsWithValue(0)); + // Receive the packet. + char received[3]; + EXPECT_THAT(read(t_, received, sizeof(received)), + SyscallSucceedsWithValue(0)); + EXPECT_THAT(read(t_, received, sizeof(received)), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(UdpSocketTest, SendAndReceiveNotConnected) { + // Bind s_ to loopback. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Send some data to s_. + char buf[512]; + RandomizeBuffer(buf, sizeof(buf)); + + ASSERT_THAT(sendto(t_, buf, sizeof(buf), 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + + // Receive the data. + char received[512]; + EXPECT_THAT(recv(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(sizeof(received))); + EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); +} + +TEST_P(UdpSocketTest, SendAndReceiveConnected) { + // Bind s_ to loopback:TestPort, and connect to loopback:TestPort+1. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(s_, addr_[1], addrlen_), SyscallSucceeds()); + + // Bind t_ to loopback:TestPort+1. + ASSERT_THAT(bind(t_, addr_[1], addrlen_), SyscallSucceeds()); + + // Send some data from t_ to s_. + char buf[512]; + RandomizeBuffer(buf, sizeof(buf)); + + ASSERT_THAT(sendto(t_, buf, sizeof(buf), 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + + // Receive the data. + char received[512]; + EXPECT_THAT(recv(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(sizeof(received))); + EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); +} + +TEST_P(UdpSocketTest, ReceiveFromNotConnected) { + // Bind s_ to loopback:TestPort, and connect to loopback:TestPort+1. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(s_, addr_[1], addrlen_), SyscallSucceeds()); + + // Bind t_ to loopback:TestPort+2. + ASSERT_THAT(bind(t_, addr_[2], addrlen_), SyscallSucceeds()); + + // Send some data from t_ to s_. + char buf[512]; + ASSERT_THAT(sendto(t_, buf, sizeof(buf), 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + + // Check that the data isn't_ received because it was sent from a different + // address than we're connected. + EXPECT_THAT(recv(s_, buf, sizeof(buf), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); +} + +TEST_P(UdpSocketTest, ReceiveBeforeConnect) { + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Bind t_ to loopback:TestPort+2. + ASSERT_THAT(bind(t_, addr_[2], addrlen_), SyscallSucceeds()); + + // Send some data from t_ to s_. + char buf[512]; + RandomizeBuffer(buf, sizeof(buf)); + + ASSERT_THAT(sendto(t_, buf, sizeof(buf), 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + + // Connect to loopback:TestPort+1. + ASSERT_THAT(connect(s_, addr_[1], addrlen_), SyscallSucceeds()); + + // Receive the data. It works because it was sent before the connect. + char received[512]; + EXPECT_THAT(recv(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(sizeof(received))); + EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); + + // Send again. This time it should not be received. + ASSERT_THAT(sendto(t_, buf, sizeof(buf), 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + + EXPECT_THAT(recv(s_, buf, sizeof(buf), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); +} + +TEST_P(UdpSocketTest, ReceiveFrom) { + // Bind s_ to loopback:TestPort, and connect to loopback:TestPort+1. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(s_, addr_[1], addrlen_), SyscallSucceeds()); + + // Bind t_ to loopback:TestPort+1. + ASSERT_THAT(bind(t_, addr_[1], addrlen_), SyscallSucceeds()); + + // Send some data from t_ to s_. + char buf[512]; + RandomizeBuffer(buf, sizeof(buf)); + + ASSERT_THAT(sendto(t_, buf, sizeof(buf), 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + + // Receive the data and sender address. + char received[512]; + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + EXPECT_THAT(recvfrom(s_, received, sizeof(received), 0, + reinterpret_cast(&addr), &addrlen), + SyscallSucceedsWithValue(sizeof(received))); + EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); + EXPECT_EQ(addrlen, addrlen_); + EXPECT_EQ(memcmp(&addr, addr_[1], addrlen_), 0); +} + +TEST_P(UdpSocketTest, Listen) { + ASSERT_THAT(listen(s_, SOMAXCONN), SyscallFailsWithErrno(EOPNOTSUPP)); +} + +TEST_P(UdpSocketTest, Accept) { + ASSERT_THAT(accept(s_, nullptr, nullptr), SyscallFailsWithErrno(EOPNOTSUPP)); +} + +// This test validates that a read shutdown with pending data allows the read +// to proceed with the data before returning EAGAIN. +TEST_P(UdpSocketTest, ReadShutdownNonblockPendingData) { + char received[512]; + + // Bind t_ to loopback:TestPort+2. + ASSERT_THAT(bind(t_, addr_[2], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(t_, addr_[1], addrlen_), SyscallSucceeds()); + + // Connect the socket, then try to shutdown again. + ASSERT_THAT(bind(s_, addr_[1], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(s_, addr_[2], addrlen_), SyscallSucceeds()); + + // Verify that we get EWOULDBLOCK when there is nothing to read. + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + const char* buf = "abc"; + EXPECT_THAT(write(t_, buf, 3), SyscallSucceedsWithValue(3)); + + int opts = 0; + ASSERT_THAT(opts = fcntl(s_, F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(s_, F_SETFL, opts | O_NONBLOCK), SyscallSucceeds()); + ASSERT_THAT(opts = fcntl(s_, F_GETFL), SyscallSucceeds()); + ASSERT_NE(opts & O_NONBLOCK, 0); + + EXPECT_THAT(shutdown(s_, SHUT_RD), SyscallSucceeds()); + + // We should get the data even though read has been shutdown. + EXPECT_THAT(recv(s_, received, 2, 0), SyscallSucceedsWithValue(2)); + + // Because we read less than the entire packet length, since it's a packet + // based socket any subsequent reads should return EWOULDBLOCK. + EXPECT_THAT(recv(s_, received, 1, 0), SyscallFailsWithErrno(EWOULDBLOCK)); +} + +// This test is validating that even after a socket is shutdown if it's +// reconnected it will reset the shutdown state. +TEST_P(UdpSocketTest, ReadShutdownSameSocketResetsShutdownState) { + char received[512]; + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + EXPECT_THAT(shutdown(s_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN)); + + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Connect the socket, then try to shutdown again. + ASSERT_THAT(bind(s_, addr_[1], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(s_, addr_[2], addrlen_), SyscallSucceeds()); + + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); +} + +TEST_P(UdpSocketTest, ReadShutdown) { + char received[512]; + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + EXPECT_THAT(shutdown(s_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN)); + + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Connect the socket, then try to shutdown again. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + EXPECT_THAT(shutdown(s_, SHUT_RD), SyscallSucceeds()); + + EXPECT_THAT(recv(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(0)); +} + +TEST_P(UdpSocketTest, ReadShutdownDifferentThread) { + char received[512]; + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Connect the socket, then shutdown from another thread. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + EXPECT_THAT(recv(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + ScopedThread t([&] { + absl::SleepFor(absl::Milliseconds(200)); + EXPECT_THAT(shutdown(this->s_, SHUT_RD), SyscallSucceeds()); + }); + EXPECT_THAT(RetryEINTR(recv)(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(0)); + t.Join(); + + EXPECT_THAT(RetryEINTR(recv)(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(0)); +} + +TEST_P(UdpSocketTest, WriteShutdown) { + EXPECT_THAT(shutdown(s_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + EXPECT_THAT(shutdown(s_, SHUT_WR), SyscallSucceeds()); +} + +TEST_P(UdpSocketTest, SynchronousReceive) { + // Bind s_ to loopback. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Send some data to s_ from another thread. + char buf[512]; + RandomizeBuffer(buf, sizeof(buf)); + + // Receive the data prior to actually starting the other thread. + char received[512]; + EXPECT_THAT(RetryEINTR(recv)(s_, received, sizeof(received), MSG_DONTWAIT), + SyscallFailsWithErrno(EWOULDBLOCK)); + + // Start the thread. + ScopedThread t([&] { + absl::SleepFor(absl::Milliseconds(200)); + ASSERT_THAT( + sendto(this->t_, buf, sizeof(buf), 0, this->addr_[0], this->addrlen_), + SyscallSucceedsWithValue(sizeof(buf))); + }); + + EXPECT_THAT(RetryEINTR(recv)(s_, received, sizeof(received), 0), + SyscallSucceedsWithValue(512)); + EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); +} + +TEST_P(UdpSocketTest, BoundaryPreserved_SendRecv) { + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Send 3 packets from t_ to s_. + constexpr int psize = 100; + char buf[3 * psize]; + RandomizeBuffer(buf, sizeof(buf)); + + for (int i = 0; i < 3; ++i) { + ASSERT_THAT(sendto(t_, buf + i * psize, psize, 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(psize)); + } + + // Receive the data as 3 separate packets. + char received[6 * psize]; + for (int i = 0; i < 3; ++i) { + EXPECT_THAT(recv(s_, received + i * psize, 3 * psize, 0), + SyscallSucceedsWithValue(psize)); + } + EXPECT_EQ(memcmp(buf, received, 3 * psize), 0); +} + +TEST_P(UdpSocketTest, BoundaryPreserved_WritevReadv) { + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Direct writes from t_ to s_. + ASSERT_THAT(connect(t_, addr_[0], addrlen_), SyscallSucceeds()); + + // Send 2 packets from t_ to s_, where each packet's data consists of 2 + // discontiguous iovecs. + constexpr size_t kPieceSize = 100; + char buf[4 * kPieceSize]; + RandomizeBuffer(buf, sizeof(buf)); + + for (int i = 0; i < 2; i++) { + struct iovec iov[2]; + for (int j = 0; j < 2; j++) { + iov[j].iov_base = reinterpret_cast( + reinterpret_cast(buf) + (i + 2 * j) * kPieceSize); + iov[j].iov_len = kPieceSize; + } + ASSERT_THAT(writev(t_, iov, 2), SyscallSucceedsWithValue(2 * kPieceSize)); + } + + // Receive the data as 2 separate packets. + char received[6 * kPieceSize]; + for (int i = 0; i < 2; i++) { + struct iovec iov[3]; + for (int j = 0; j < 3; j++) { + iov[j].iov_base = reinterpret_cast( + reinterpret_cast(received) + (i + 2 * j) * kPieceSize); + iov[j].iov_len = kPieceSize; + } + ASSERT_THAT(readv(s_, iov, 3), SyscallSucceedsWithValue(2 * kPieceSize)); + } + EXPECT_EQ(memcmp(buf, received, 4 * kPieceSize), 0); +} + +TEST_P(UdpSocketTest, BoundaryPreserved_SendMsgRecvMsg) { + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Send 2 packets from t_ to s_, where each packet's data consists of 2 + // discontiguous iovecs. + constexpr size_t kPieceSize = 100; + char buf[4 * kPieceSize]; + RandomizeBuffer(buf, sizeof(buf)); + + for (int i = 0; i < 2; i++) { + struct iovec iov[2]; + for (int j = 0; j < 2; j++) { + iov[j].iov_base = reinterpret_cast( + reinterpret_cast(buf) + (i + 2 * j) * kPieceSize); + iov[j].iov_len = kPieceSize; + } + struct msghdr msg = {}; + msg.msg_name = addr_[0]; + msg.msg_namelen = addrlen_; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + ASSERT_THAT(sendmsg(t_, &msg, 0), SyscallSucceedsWithValue(2 * kPieceSize)); + } + + // Receive the data as 2 separate packets. + char received[6 * kPieceSize]; + for (int i = 0; i < 2; i++) { + struct iovec iov[3]; + for (int j = 0; j < 3; j++) { + iov[j].iov_base = reinterpret_cast( + reinterpret_cast(received) + (i + 2 * j) * kPieceSize); + iov[j].iov_len = kPieceSize; + } + struct msghdr msg = {}; + msg.msg_iov = iov; + msg.msg_iovlen = 3; + ASSERT_THAT(recvmsg(s_, &msg, 0), SyscallSucceedsWithValue(2 * kPieceSize)); + } + EXPECT_EQ(memcmp(buf, received, 4 * kPieceSize), 0); +} + +TEST_P(UdpSocketTest, FIONREADShutdown) { + int n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + // A UDP socket must be connected before it can be shutdown. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + EXPECT_THAT(shutdown(s_, SHUT_RD), SyscallSucceeds()); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); +} + +TEST_P(UdpSocketTest, FIONREADWriteShutdown) { + int n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // A UDP socket must be connected before it can be shutdown. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + const char str[] = "abc"; + ASSERT_THAT(send(s_, str, sizeof(str), 0), + SyscallSucceedsWithValue(sizeof(str))); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, sizeof(str)); + + EXPECT_THAT(shutdown(s_, SHUT_RD), SyscallSucceeds()); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, sizeof(str)); +} + +TEST_P(UdpSocketTest, FIONREAD) { + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Check that the bound socket with an empty buffer reports an empty first + // packet. + int n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + // Send 3 packets from t_ to s_. + constexpr int psize = 100; + char buf[3 * psize]; + RandomizeBuffer(buf, sizeof(buf)); + + for (int i = 0; i < 3; ++i) { + ASSERT_THAT(sendto(t_, buf + i * psize, psize, 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(psize)); + + // Check that regardless of how many packets are in the queue, the size + // reported is that of a single packet. + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, psize); + } +} + +TEST_P(UdpSocketTest, FIONREADZeroLengthPacket) { + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // Check that the bound socket with an empty buffer reports an empty first + // packet. + int n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + // Send 3 packets from t_ to s_. + constexpr int psize = 100; + char buf[3 * psize]; + RandomizeBuffer(buf, sizeof(buf)); + + for (int i = 0; i < 3; ++i) { + ASSERT_THAT(sendto(t_, buf + i * psize, 0, 0, addr_[0], addrlen_), + SyscallSucceedsWithValue(0)); + + // Check that regardless of how many packets are in the queue, the size + // reported is that of a single packet. + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + } +} + +TEST_P(UdpSocketTest, FIONREADZeroLengthWriteShutdown) { + int n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + // Bind s_ to loopback:TestPort. + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + + // A UDP socket must be connected before it can be shutdown. + ASSERT_THAT(connect(s_, addr_[0], addrlen_), SyscallSucceeds()); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + const char str[] = "abc"; + ASSERT_THAT(send(s_, str, 0, 0), SyscallSucceedsWithValue(0)); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + EXPECT_THAT(shutdown(s_, SHUT_RD), SyscallSucceeds()); + + n = -1; + EXPECT_THAT(ioctl(s_, FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); +} + +TEST_P(UdpSocketTest, ErrorQueue) { + char cmsgbuf[CMSG_SPACE(sizeof(sock_extended_err))]; + msghdr msg; + memset(&msg, 0, sizeof(msg)); + iovec iov; + memset(&iov, 0, sizeof(iov)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + // recv*(MSG_ERRQUEUE) never blocks, even without MSG_DONTWAIT. + EXPECT_THAT(RetryEINTR(recvmsg)(s_, &msg, MSG_ERRQUEUE), + SyscallFailsWithErrno(EAGAIN)); +} + +TEST_P(UdpSocketTest, SoTimestamp) { + ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds()); + ASSERT_THAT(connect(t_, addr_[0], addrlen_), SyscallSucceeds()); + + int v = 1; + EXPECT_THAT(setsockopt(s_, SOL_SOCKET, SO_TIMESTAMP, &v, sizeof(v)), + SyscallSucceeds()); + + char buf[3]; + // Send zero length packet from t_ to s_. + ASSERT_THAT(RetryEINTR(write)(t_, buf, 0), SyscallSucceedsWithValue(0)); + + char cmsgbuf[CMSG_SPACE(sizeof(struct timeval))]; + msghdr msg; + memset(&msg, 0, sizeof(msg)); + iovec iov; + memset(&iov, 0, sizeof(iov)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + ASSERT_THAT(RetryEINTR(recvmsg)(s_, &msg, 0), SyscallSucceedsWithValue(0)); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg, nullptr); + ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg->cmsg_type, SO_TIMESTAMP); + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(struct timeval))); + + struct timeval tv = {}; + memcpy(&tv, CMSG_DATA(cmsg), sizeof(struct timeval)); + + ASSERT_TRUE(tv.tv_sec != 0 || tv.tv_usec != 0); +} + +TEST_P(UdpSocketTest, WriteShutdownNotConnected) { + EXPECT_THAT(shutdown(s_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); +} + +INSTANTIATE_TEST_CASE_P(AllInetTests, UdpSocketTest, + ::testing::Values(AF_INET, AF_INET6)); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/uidgid.cc b/test/syscalls/linux/uidgid.cc new file mode 100644 index 000000000..c0c1f2960 --- /dev/null +++ b/test/syscalls/linux/uidgid.cc @@ -0,0 +1,277 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "test/util/capability_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +DEFINE_int32(scratch_uid1, 65534, "first scratch UID"); +DEFINE_int32(scratch_uid2, 65533, "second scratch UID"); +DEFINE_int32(scratch_gid1, 65534, "first scratch GID"); +DEFINE_int32(scratch_gid2, 65533, "second scratch GID"); + +using ::testing::UnorderedElementsAreArray; + +namespace gvisor { +namespace testing { + +namespace { + +TEST(UidGidTest, Getuid) { + uid_t ruid, euid, suid; + EXPECT_THAT(getresuid(&ruid, &euid, &suid), SyscallSucceeds()); + EXPECT_THAT(getuid(), SyscallSucceedsWithValue(ruid)); + EXPECT_THAT(geteuid(), SyscallSucceedsWithValue(euid)); +} + +TEST(UidGidTest, Getgid) { + gid_t rgid, egid, sgid; + EXPECT_THAT(getresgid(&rgid, &egid, &sgid), SyscallSucceeds()); + EXPECT_THAT(getgid(), SyscallSucceedsWithValue(rgid)); + EXPECT_THAT(getegid(), SyscallSucceedsWithValue(egid)); +} + +TEST(UidGidTest, Getgroups) { + // "If size is zero, list is not modified, but the total number of + // supplementary group IDs for the process is returned." - getgroups(2) + int nr_groups; + ASSERT_THAT(nr_groups = getgroups(0, nullptr), SyscallSucceeds()); + std::vector list(nr_groups); + EXPECT_THAT(getgroups(list.size(), list.data()), SyscallSucceeds()); + + // "EINVAL: size is less than the number of supplementary group IDs, but is + // not zero." + EXPECT_THAT(getgroups(-1, nullptr), SyscallFailsWithErrno(EINVAL)); + + // Testing for EFAULT requires actually having groups, which isn't guaranteed + // here; see the setgroups test below. +} + +// If the caller's real/effective/saved user/group IDs are all 0, IsRoot returns +// true. Otherwise IsRoot logs an explanatory message and returns false. +PosixErrorOr IsRoot() { + uid_t ruid, euid, suid; + int rc = getresuid(&ruid, &euid, &suid); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "getresuid"); + } + if (ruid != 0 || euid != 0 || suid != 0) { + return false; + } + gid_t rgid, egid, sgid; + rc = getresgid(&rgid, &egid, &sgid); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "getresgid"); + } + if (rgid != 0 || egid != 0 || sgid != 0) { + return false; + } + return true; +} + +// Checks that the calling process' real/effective/saved user IDs are +// ruid/euid/suid respectively. +PosixError CheckUIDs(uid_t ruid, uid_t euid, uid_t suid) { + uid_t actual_ruid, actual_euid, actual_suid; + int rc = getresuid(&actual_ruid, &actual_euid, &actual_suid); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "getresuid"); + } + if (ruid != actual_ruid || euid != actual_euid || suid != actual_suid) { + return PosixError( + EPERM, absl::StrCat( + "incorrect user IDs: got (", + absl::StrJoin({actual_ruid, actual_euid, actual_suid}, ", "), + ", wanted (", absl::StrJoin({ruid, euid, suid}, ", "), ")")); + } + return NoError(); +} + +PosixError CheckGIDs(gid_t rgid, gid_t egid, gid_t sgid) { + gid_t actual_rgid, actual_egid, actual_sgid; + int rc = getresgid(&actual_rgid, &actual_egid, &actual_sgid); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "getresgid"); + } + if (rgid != actual_rgid || egid != actual_egid || sgid != actual_sgid) { + return PosixError( + EPERM, absl::StrCat( + "incorrect group IDs: got (", + absl::StrJoin({actual_rgid, actual_egid, actual_sgid}, ", "), + ", wanted (", absl::StrJoin({rgid, egid, sgid}, ", "), ")")); + } + return NoError(); +} + +// N.B. These tests may break horribly unless run via a gVisor test runner, +// because changing UID in one test may forfeit permissions required by other +// tests. (The test runner runs each test in a separate process.) + +TEST(UidGidRootTest, Setuid) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + // Do setuid in a separate thread so that after finishing this test, the + // process can still open files the test harness created before starting this + // test. Otherwise, the files are created by root (UID before the test), but + // cannot be opened by the `uid` set below after the test. After calling + // setuid(non-zero-UID), there is no way to get root privileges back. + ScopedThread([&] { + // Use syscall instead of glibc setuid wrapper because we want this setuid + // call to only apply to this task. POSIX threads, however, require that all + // threads have the same UIDs, so using the setuid wrapper sets all threads' + // real UID. + EXPECT_THAT(syscall(SYS_setuid, -1), SyscallFailsWithErrno(EINVAL)); + + const uid_t uid = FLAGS_scratch_uid1; + EXPECT_THAT(syscall(SYS_setuid, uid), SyscallSucceeds()); + // "If the effective UID of the caller is root (more precisely: if the + // caller has the CAP_SETUID capability), the real UID and saved set-user-ID + // are also set." - setuid(2) + EXPECT_NO_ERRNO(CheckUIDs(uid, uid, uid)); + }); +} + +TEST(UidGidRootTest, Setgid) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + EXPECT_THAT(setgid(-1), SyscallFailsWithErrno(EINVAL)); + + const gid_t gid = FLAGS_scratch_gid1; + ASSERT_THAT(setgid(gid), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); +} + +TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + const gid_t gid = FLAGS_scratch_gid1; + // NOTE: Do setgid in a separate thread so that we can test if + // info.si_pid is set correctly. + ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); }); + EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); +} + +TEST(UidGidRootTest, Setreuid) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + // "Supplying a value of -1 for either the real or effective user ID forces + // the system to leave that ID unchanged." - setreuid(2) + EXPECT_THAT(setreuid(-1, -1), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); + + // Do setuid in a separate thread so that after finishing this test, the + // process can still open files the test harness created before starting this + // test. Otherwise, the files are created by root (UID before the test), but + // cannot be opened by the `uid` set below after the test. After calling + // setuid(non-zero-UID), there is no way to get root privileges back. + ScopedThread([&] { + const uid_t ruid = FLAGS_scratch_uid1; + const uid_t euid = FLAGS_scratch_uid2; + + // Use syscall instead of glibc setuid wrapper because we want this setuid + // call to only apply to this task. posix threads, however, require that all + // threads have the same UIDs, so using the setuid wrapper sets all threads' + // real UID. + EXPECT_THAT(syscall(SYS_setreuid, ruid, euid), SyscallSucceeds()); + + // "If the real user ID is set or the effective user ID is set to a value + // not equal to the previous real user ID, the saved set-user-ID will be set + // to the new effective user ID." - setreuid(2) + EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, euid)); + }); +} + +TEST(UidGidRootTest, Setregid) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + EXPECT_THAT(setregid(-1, -1), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); + + const gid_t rgid = FLAGS_scratch_gid1; + const gid_t egid = FLAGS_scratch_gid2; + ASSERT_THAT(setregid(rgid, egid), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, egid)); +} + +TEST(UidGidRootTest, Setresuid) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + // "If one of the arguments equals -1, the corresponding value is not + // changed." - setresuid(2) + EXPECT_THAT(setresuid(-1, -1, -1), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); + + // Do setuid in a separate thread so that after finishing this test, the + // process can still open files the test harness created before starting this + // test. Otherwise, the files are created by root (UID before the test), but + // cannot be opened by the `uid` set below after the test. After calling + // setuid(non-zero-UID), there is no way to get root privileges back. + ScopedThread([&] { + const uid_t ruid = 12345; + const uid_t euid = 23456; + const uid_t suid = 34567; + + // Use syscall instead of glibc setuid wrapper because we want this setuid + // call to only apply to this task. posix threads, however, require that all + // threads have the same UIDs, so using the setuid wrapper sets all threads' + // real UID. + EXPECT_THAT(syscall(SYS_setresuid, ruid, euid, suid), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, suid)); + }); +} + +TEST(UidGidRootTest, Setresgid) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + EXPECT_THAT(setresgid(-1, -1, -1), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); + + const gid_t rgid = 12345; + const gid_t egid = 23456; + const gid_t sgid = 34567; + ASSERT_THAT(setresgid(rgid, egid, sgid), SyscallSucceeds()); + EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, sgid)); +} + +TEST(UidGidRootTest, Setgroups) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); + + std::vector list = {123, 500}; + ASSERT_THAT(setgroups(list.size(), list.data()), SyscallSucceeds()); + std::vector list2(list.size()); + ASSERT_THAT(getgroups(list2.size(), list2.data()), SyscallSucceeds()); + EXPECT_THAT(list, UnorderedElementsAreArray(list2)); + + // "EFAULT: list has an invalid address." + EXPECT_THAT(getgroups(100, reinterpret_cast(-1)), + SyscallFailsWithErrno(EFAULT)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/uname.cc b/test/syscalls/linux/uname.cc new file mode 100644 index 000000000..d22a34bd7 --- /dev/null +++ b/test/syscalls/linux/uname.cc @@ -0,0 +1,99 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "test/util/capability_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(UnameTest, Sanity) { + struct utsname buf; + ASSERT_THAT(uname(&buf), SyscallSucceeds()); + EXPECT_NE(strlen(buf.release), 0); + EXPECT_NE(strlen(buf.version), 0); + EXPECT_NE(strlen(buf.machine), 0); + EXPECT_NE(strlen(buf.sysname), 0); + EXPECT_NE(strlen(buf.nodename), 0); + EXPECT_NE(strlen(buf.domainname), 0); +} + +TEST(UnameTest, SetNames) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + constexpr char kHostname[] = "wubbalubba"; + ASSERT_THAT(sethostname(kHostname, sizeof(kHostname)), SyscallSucceeds()); + + constexpr char kDomainname[] = "dubdub.com"; + ASSERT_THAT(setdomainname(kDomainname, sizeof(kDomainname)), + SyscallSucceeds()); + + struct utsname buf; + EXPECT_THAT(uname(&buf), SyscallSucceeds()); + EXPECT_EQ(absl::string_view(buf.nodename), kHostname); + EXPECT_EQ(absl::string_view(buf.domainname), kDomainname); + + // These should just be glibc wrappers that also call uname(2). + char hostname[65]; + EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds()); + EXPECT_EQ(absl::string_view(hostname), kHostname); + + char domainname[65]; + EXPECT_THAT(getdomainname(domainname, sizeof(domainname)), SyscallSucceeds()); + EXPECT_EQ(absl::string_view(domainname), kDomainname); +} + +TEST(UnameTest, UnprivilegedSetNames) { + if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))) { + EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); + } + + EXPECT_THAT(sethostname("", 0), SyscallFailsWithErrno(EPERM)); + EXPECT_THAT(setdomainname("", 0), SyscallFailsWithErrno(EPERM)); +} + +TEST(UnameTest, UnshareUTS) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + struct utsname init; + ASSERT_THAT(uname(&init), SyscallSucceeds()); + + ScopedThread([&]() { + EXPECT_THAT(unshare(CLONE_NEWUTS), SyscallSucceeds()); + + constexpr char kHostname[] = "wubbalubba"; + EXPECT_THAT(sethostname(kHostname, sizeof(kHostname)), SyscallSucceeds()); + + char hostname[65]; + EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds()); + }); + + struct utsname after; + EXPECT_THAT(uname(&after), SyscallSucceeds()); + EXPECT_EQ(absl::string_view(after.nodename), init.nodename); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/unix_domain_socket_test_util.cc b/test/syscalls/linux/unix_domain_socket_test_util.cc new file mode 100644 index 000000000..2d7a530b9 --- /dev/null +++ b/test/syscalls/linux/unix_domain_socket_test_util.cc @@ -0,0 +1,346 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/syscalls/linux/unix_domain_socket_test_util.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::string DescribeUnixDomainSocketType(int type) { + const char* type_str = nullptr; + switch (type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) { + case SOCK_STREAM: + type_str = "SOCK_STREAM"; + break; + case SOCK_DGRAM: + type_str = "SOCK_DGRAM"; + break; + case SOCK_SEQPACKET: + type_str = "SOCK_SEQPACKET"; + break; + } + if (!type_str) { + return absl::StrCat("Unix domain socket with unknown type ", type); + } else { + return absl::StrCat(((type & SOCK_NONBLOCK) != 0) ? "non-blocking " : "", + ((type & SOCK_CLOEXEC) != 0) ? "close-on-exec " : "", + type_str, " Unix domain socket"); + } +} + +SocketPairKind UnixDomainSocketPair(int type) { + return SocketPairKind{DescribeUnixDomainSocketType(type), + SyscallSocketPairCreator(AF_UNIX, type, 0)}; +} + +SocketPairKind FilesystemBoundUnixDomainSocketPair(int type) { + std::string description = absl::StrCat(DescribeUnixDomainSocketType(type), + " created with filesystem binding"); + if ((type & SOCK_DGRAM) == SOCK_DGRAM) { + return SocketPairKind{ + description, + FilesystemBidirectionalBindSocketPairCreator(AF_UNIX, type, 0)}; + } + return SocketPairKind{ + description, FilesystemAcceptBindSocketPairCreator(AF_UNIX, type, 0)}; +} + +SocketPairKind AbstractBoundUnixDomainSocketPair(int type) { + std::string description = absl::StrCat(DescribeUnixDomainSocketType(type), + " created with abstract namespace binding"); + if ((type & SOCK_DGRAM) == SOCK_DGRAM) { + return SocketPairKind{ + description, + AbstractBidirectionalBindSocketPairCreator(AF_UNIX, type, 0)}; + } + return SocketPairKind{description, + AbstractAcceptBindSocketPairCreator(AF_UNIX, type, 0)}; +} + +SocketPairKind SocketpairGoferUnixDomainSocketPair(int type) { + std::string description = absl::StrCat(DescribeUnixDomainSocketType(type), + " created with the socketpair gofer"); + return SocketPairKind{description, + SocketpairGoferSocketPairCreator(AF_UNIX, type, 0)}; +} + +SocketPairKind SocketpairGoferFileSocketPair(int type) { + std::string description = + absl::StrCat(((type & O_NONBLOCK) != 0) ? "non-blocking " : "", + ((type & O_CLOEXEC) != 0) ? "close-on-exec " : "", + "file socket created with the socketpair gofer"); + return SocketPairKind{description, + SocketpairGoferFileSocketPairCreator(type)}; +} + +SocketPairKind FilesystemUnboundUnixDomainSocketPair(int type) { + return SocketPairKind{absl::StrCat(DescribeUnixDomainSocketType(type), + " unbound with a filesystem address"), + FilesystemUnboundSocketPairCreator(AF_UNIX, type, 0)}; +} + +SocketPairKind AbstractUnboundUnixDomainSocketPair(int type) { + return SocketPairKind{ + absl::StrCat(DescribeUnixDomainSocketType(type), + " unbound with an abstract namespace address"), + AbstractUnboundSocketPairCreator(AF_UNIX, type, 0)}; +} + +void SendSingleFD(int sock, int fd, char buf[], int buf_size) { + ASSERT_NO_FATAL_FAILURE(SendFDs(sock, &fd, 1, buf, buf_size)); +} + +void SendFDs(int sock, int fds[], int fds_size, char buf[], int buf_size) { + struct msghdr msg = {}; + std::vector control(CMSG_SPACE(fds_size * sizeof(int))); + msg.msg_control = &control[0]; + msg.msg_controllen = control.size(); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(fds_size * sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + for (int i = 0; i < fds_size; i++) { + memcpy(CMSG_DATA(cmsg) + i * sizeof(int), &fds[i], sizeof(int)); + } + + ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), + IsPosixErrorOkAndHolds(buf_size)); +} + +void RecvSingleFD(int sock, int* fd, char buf[], int buf_size) { + ASSERT_NO_FATAL_FAILURE(RecvFDs(sock, fd, 1, buf, buf_size, buf_size)); +} + +void RecvSingleFD(int sock, int* fd, char buf[], int buf_size, + int expected_size) { + ASSERT_NO_FATAL_FAILURE(RecvFDs(sock, fd, 1, buf, buf_size, expected_size)); +} + +void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size) { + ASSERT_NO_FATAL_FAILURE( + RecvFDs(sock, fds, fds_size, buf, buf_size, buf_size)); +} + +void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size, + int expected_size, bool peek) { + struct msghdr msg = {}; + std::vector control(CMSG_SPACE(fds_size * sizeof(int))); + msg.msg_control = &control[0]; + msg.msg_controllen = control.size(); + + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + int flags = 0; + if (peek) { + flags |= MSG_PEEK; + } + + ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, flags), + SyscallSucceedsWithValue(expected_size)); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg, nullptr); + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(fds_size * sizeof(int))); + ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); + + for (int i = 0; i < fds_size; i++) { + memcpy(&fds[i], CMSG_DATA(cmsg) + i * sizeof(int), sizeof(int)); + } +} + +void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size, + int expected_size) { + ASSERT_NO_FATAL_FAILURE( + RecvFDs(sock, fds, fds_size, buf, buf_size, expected_size, false)); +} + +void PeekSingleFD(int sock, int* fd, char buf[], int buf_size) { + ASSERT_NO_FATAL_FAILURE(RecvFDs(sock, fd, 1, buf, buf_size, buf_size, true)); +} + +void RecvNoCmsg(int sock, char buf[], int buf_size, int expected_size) { + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), + SyscallSucceedsWithValue(expected_size)); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + EXPECT_EQ(cmsg, nullptr); +} + +void SendNullCmsg(int sock, char buf[], int buf_size) { + struct msghdr msg = {}; + msg.msg_control = nullptr; + msg.msg_controllen = 0; + + ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), + IsPosixErrorOkAndHolds(buf_size)); +} + +void SendCreds(int sock, ucred creds, char buf[], int buf_size) { + struct msghdr msg = {}; + + char control[CMSG_SPACE(sizeof(struct ucred))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), &creds, sizeof(struct ucred)); + + ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), + IsPosixErrorOkAndHolds(buf_size)); +} + +void SendCredsAndFD(int sock, ucred creds, int fd, char buf[], int buf_size) { + struct msghdr msg = {}; + + char control[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))] = {}; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct cmsghdr* cmsg1 = CMSG_FIRSTHDR(&msg); + cmsg1->cmsg_level = SOL_SOCKET; + cmsg1->cmsg_type = SCM_CREDENTIALS; + cmsg1->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg1), &creds, sizeof(struct ucred)); + + struct cmsghdr* cmsg2 = CMSG_NXTHDR(&msg, cmsg1); + cmsg2->cmsg_level = SOL_SOCKET; + cmsg2->cmsg_type = SCM_RIGHTS; + cmsg2->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg2), &fd, sizeof(int)); + + ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), + IsPosixErrorOkAndHolds(buf_size)); +} + +void RecvCreds(int sock, ucred* creds, char buf[], int buf_size) { + ASSERT_NO_FATAL_FAILURE(RecvCreds(sock, creds, buf, buf_size, buf_size)); +} + +void RecvCreds(int sock, ucred* creds, char buf[], int buf_size, + int expected_size) { + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(struct ucred))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), + SyscallSucceedsWithValue(expected_size)); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg, nullptr); + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(struct ucred))); + ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); + + memcpy(creds, CMSG_DATA(cmsg), sizeof(struct ucred)); +} + +void RecvCredsAndFD(int sock, ucred* creds, int* fd, char buf[], int buf_size) { + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), + SyscallSucceedsWithValue(buf_size)); + + struct cmsghdr* cmsg1 = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg1, nullptr); + ASSERT_EQ(cmsg1->cmsg_len, CMSG_LEN(sizeof(struct ucred))); + ASSERT_EQ(cmsg1->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg1->cmsg_type, SCM_CREDENTIALS); + memcpy(creds, CMSG_DATA(cmsg1), sizeof(struct ucred)); + + struct cmsghdr* cmsg2 = CMSG_NXTHDR(&msg, cmsg1); + ASSERT_NE(cmsg2, nullptr); + ASSERT_EQ(cmsg2->cmsg_len, CMSG_LEN(sizeof(int))); + ASSERT_EQ(cmsg2->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg2->cmsg_type, SCM_RIGHTS); + memcpy(fd, CMSG_DATA(cmsg2), sizeof(int)); +} + +void RecvSingleFDUnaligned(int sock, int* fd, char buf[], int buf_size) { + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(int)) - sizeof(int)]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), + SyscallSucceedsWithValue(buf_size)); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(cmsg, nullptr); + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); + ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); + ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); + + memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); +} + +void SetSoPassCred(int sock) { + int one = 1; + EXPECT_THAT(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)), + SyscallSucceeds()); +} + +void UnsetSoPassCred(int sock) { + int zero = 0; + EXPECT_THAT(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &zero, sizeof(zero)), + SyscallSucceeds()); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/unix_domain_socket_test_util.h b/test/syscalls/linux/unix_domain_socket_test_util.h new file mode 100644 index 000000000..1b09aeae7 --- /dev/null +++ b/test/syscalls/linux/unix_domain_socket_test_util.h @@ -0,0 +1,161 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_SYSCALLS_UNIX_DOMAIN_SOCKET_TEST_UTIL_H_ +#define GVISOR_TEST_SYSCALLS_UNIX_DOMAIN_SOCKET_TEST_UTIL_H_ + +#include +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// DescribeUnixDomainSocketType returns a human-readable std::string explaining the +// given Unix domain socket type. +std::string DescribeUnixDomainSocketType(int type); + +// UnixDomainSocketPair returns a SocketPairKind that represents SocketPairs +// created by invoking the socketpair() syscall with AF_UNIX and the given type. +SocketPairKind UnixDomainSocketPair(int type); + +// FilesystemBoundUnixDomainSocketPair returns a SocketPairKind that represents +// SocketPairs created with bind() and accept() syscalls with a temp file path, +// AF_UNIX and the given type. +SocketPairKind FilesystemBoundUnixDomainSocketPair(int type); + +// AbstractBoundUnixDomainSocketPair returns a SocketPairKind that represents +// SocketPairs created with bind() and accept() syscalls with a temp abstract +// path, AF_UNIX and the given type. +SocketPairKind AbstractBoundUnixDomainSocketPair(int type); + +// SocketpairGoferUnixDomainSocketPair returns a SocketPairKind that was created +// with two sockets conected to the socketpair gofer. +SocketPairKind SocketpairGoferUnixDomainSocketPair(int type); + +// SocketpairGoferFileSocketPair returns a SocketPairKind that was created with +// two open() calls on paths backed by the socketpair gofer. +SocketPairKind SocketpairGoferFileSocketPair(int type); + +// FilesystemUnboundUnixDomainSocketPair returns a SocketPairKind that +// represents two unbound sockets and a filesystem path for binding. +SocketPairKind FilesystemUnboundUnixDomainSocketPair(int type); + +// AbstractUnboundUnixDomainSocketPair returns a SocketPairKind that represents +// two unbound sockets and an abstract namespace path for binding. +SocketPairKind AbstractUnboundUnixDomainSocketPair(int type); + +// SendSingleFD sends both a single FD and some data over a unix domain socket +// specified by an FD. Note that calls to this function must be wrapped in +// ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. +void SendSingleFD(int sock, int fd, char buf[], int buf_size); + +// SendFDs sends an arbitrary number of FDs and some data over a unix domain +// socket specified by an FD. Note that calls to this function must be wrapped +// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. +void SendFDs(int sock, int fds[], int fds_size, char buf[], int buf_size); + +// RecvSingleFD receives both a single FD and some data over a unix domain +// socket specified by an FD. Note that calls to this function must be wrapped +// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. +void RecvSingleFD(int sock, int* fd, char buf[], int buf_size); + +// RecvSingleFD receives both a single FD and some data over a unix domain +// socket specified by an FD. This version allows the expected amount of data +// received to be different than the buffer size. Note that calls to this +// function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions +// to halt the test. +void RecvSingleFD(int sock, int* fd, char buf[], int buf_size, + int expected_size); + +// PeekSingleFD peeks at both a single FD and some data over a unix domain +// socket specified by an FD. Note that calls to this function must be wrapped +// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. +void PeekSingleFD(int sock, int* fd, char buf[], int buf_size); + +// RecvFDs receives both an arbitrary number of FDs and some data over a unix +// domain socket specified by an FD. Note that calls to this function must be +// wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. +void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size); + +// RecvFDs receives both an arbitrary number of FDs and some data over a unix +// domain socket specified by an FD. This version allows the expected amount of +// data received to be different than the buffer size. Note that calls to this +// function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions +// to halt the test. +void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size, + int expected_size); + +// RecvNoCmsg receives some data over a unix domain socket specified by an FD +// and asserts that no control messages are available for receiving. Note that +// calls to this function must be wrapped in ASSERT_NO_FATAL_FAILURE for +// internal assertions to halt the test. +void RecvNoCmsg(int sock, char buf[], int buf_size, int expected_size); + +inline void RecvNoCmsg(int sock, char buf[], int buf_size) { + RecvNoCmsg(sock, buf, buf_size, buf_size); +} + +// SendCreds sends the credentials of the current process and some data over a +// unix domain socket specified by an FD. Note that calls to this function must +// be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the +// test. +void SendCreds(int sock, ucred creds, char buf[], int buf_size); + +// SendCredsAndFD sends the credentials of the current process, a single FD, and +// some data over a unix domain socket specified by an FD. Note that calls to +// this function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal +// assertions to halt the test. +void SendCredsAndFD(int sock, ucred creds, int fd, char buf[], int buf_size); + +// RecvCreds receives some credentials and some data over a unix domain socket +// specified by an FD. Note that calls to this function must be wrapped in +// ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. +void RecvCreds(int sock, ucred* creds, char buf[], int buf_size); + +// RecvCreds receives some credentials and some data over a unix domain socket +// specified by an FD. This version allows the expected amount of data received +// to be different than the buffer size. Note that calls to this function must +// be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the +// test. +void RecvCreds(int sock, ucred* creds, char buf[], int buf_size, + int expected_size); + +// RecvCredsAndFD receives some credentials, a single FD, and some data over a +// unix domain socket specified by an FD. Note that calls to this function must +// be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the +// test. +void RecvCredsAndFD(int sock, ucred* creds, int* fd, char buf[], int buf_size); + +// SendNullCmsg sends a null control message and some data over a unix domain +// socket specified by an FD. Note that calls to this function must be wrapped +// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. +void SendNullCmsg(int sock, char buf[], int buf_size); + +// RecvSingleFDUnaligned sends both a single FD and some data over a unix domain +// socket specified by an FD. This function does not obey the spec, but Linux +// allows it and the apphosting code depends on this quirk. Note that calls to +// this function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal +// assertions to halt the test. +void RecvSingleFDUnaligned(int sock, int* fd, char buf[], int buf_size); + +// SetSoPassCred sets the SO_PASSCRED option on the specified socket. +void SetSoPassCred(int sock); + +// UnsetSoPassCred clears the SO_PASSCRED option on the specified socket. +void UnsetSoPassCred(int sock); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_UNIX_DOMAIN_SOCKET_TEST_UTIL_H_ diff --git a/test/syscalls/linux/unlink.cc b/test/syscalls/linux/unlink.cc new file mode 100644 index 000000000..4d5e0c6b6 --- /dev/null +++ b/test/syscalls/linux/unlink.cc @@ -0,0 +1,211 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(UnlinkTest, IsDir) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + EXPECT_THAT(unlink(dir.path().c_str()), SyscallFailsWithErrno(EISDIR)); +} + +TEST(UnlinkTest, DirNotEmpty) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + int fd; + std::string path = JoinPath(dir.path(), "ExistingFile"); + EXPECT_THAT(fd = open(path.c_str(), O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + EXPECT_THAT(rmdir(dir.path().c_str()), SyscallFailsWithErrno(ENOTEMPTY)); +} + +TEST(UnlinkTest, Rmdir) { + std::string path = JoinPath(GetAbsoluteTestTmpdir(), "NewDir"); + ASSERT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + EXPECT_THAT(rmdir(path.c_str()), SyscallSucceeds()); +} + +TEST(UnlinkTest, AtDir) { + int dirfd; + EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0), + SyscallSucceeds()); + + std::string path = JoinPath(GetAbsoluteTestTmpdir(), "NewDir"); + EXPECT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + EXPECT_THAT(unlinkat(dirfd, "NewDir", AT_REMOVEDIR), SyscallSucceeds()); + ASSERT_THAT(close(dirfd), SyscallSucceeds()); +} + +TEST(UnlinkTest, AtDirDegradedPermissions_NoRandomSave) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + int dirfd; + ASSERT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), + SyscallSucceeds()); + + std::string sub_dir = JoinPath(dir.path(), "NewDir"); + EXPECT_THAT(mkdir(sub_dir.c_str(), 0755), SyscallSucceeds()); + EXPECT_THAT(fchmod(dirfd, 0444), SyscallSucceeds()); + EXPECT_THAT(unlinkat(dirfd, "NewDir", AT_REMOVEDIR), + SyscallFailsWithErrno(EACCES)); + ASSERT_THAT(close(dirfd), SyscallSucceeds()); +} + +// Files cannot be unlinked if the parent is not writable and executable. +TEST(UnlinkTest, ParentDegradedPermissions) { + // Drop capabilities that allow us to override file and directory permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); + + ASSERT_THAT(chmod(dir.path().c_str(), 0000), SyscallSucceeds()); + + struct stat st; + ASSERT_THAT(stat(file.path().c_str(), &st), SyscallFailsWithErrno(EACCES)); + ASSERT_THAT(unlinkat(AT_FDCWD, file.path().c_str(), 0), + SyscallFailsWithErrno(EACCES)); + + // Non-existent files also return EACCES. + const std::string nonexist = JoinPath(dir.path(), "doesnotexist"); + ASSERT_THAT(stat(nonexist.c_str(), &st), SyscallFailsWithErrno(EACCES)); + ASSERT_THAT(unlinkat(AT_FDCWD, nonexist.c_str(), 0), + SyscallFailsWithErrno(EACCES)); +} + +TEST(UnlinkTest, AtBad) { + int dirfd; + EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0), + SyscallSucceeds()); + + // Try removing a directory as a file. + std::string path = JoinPath(GetAbsoluteTestTmpdir(), "NewDir"); + EXPECT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + EXPECT_THAT(unlinkat(dirfd, "NewDir", 0), SyscallFailsWithErrno(EISDIR)); + EXPECT_THAT(unlinkat(dirfd, "NewDir", AT_REMOVEDIR), SyscallSucceeds()); + + // Try removing a file as a directory. + int fd; + EXPECT_THAT(fd = openat(dirfd, "UnlinkAtFile", O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", AT_REMOVEDIR), + SyscallFailsWithErrno(ENOTDIR)); + ASSERT_THAT(close(fd), SyscallSucceeds()); + EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", 0), SyscallSucceeds()); + + // Cleanup. + ASSERT_THAT(close(dirfd), SyscallSucceeds()); +} + +TEST(UnlinkTest, AbsTmpFile) { + int fd; + std::string path = JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile"); + EXPECT_THAT(fd = open(path.c_str(), O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + EXPECT_THAT(unlink(path.c_str()), SyscallSucceeds()); +} + +TEST(UnlinkTest, TooLongName) { + EXPECT_THAT(unlink(std::vector(16384, '0').data()), + SyscallFailsWithErrno(ENAMETOOLONG)); +} + +TEST(UnlinkTest, BadNamePtr) { + EXPECT_THAT(unlink(reinterpret_cast(1)), + SyscallFailsWithErrno(EFAULT)); +} + +TEST(UnlinkTest, AtFile) { + int dirfd; + EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0666), + SyscallSucceeds()); + int fd; + EXPECT_THAT(fd = openat(dirfd, "UnlinkAtFile", O_RDWR | O_CREAT, 0666), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); + EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", 0), SyscallSucceeds()); +} + +TEST(UnlinkTest, OpenFile) { + // We can't save unlinked file unless they are on tmpfs. + const DisableSave ds; + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + int fd; + EXPECT_THAT(fd = open(file.path().c_str(), O_RDWR, 0666), SyscallSucceeds()); + EXPECT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST(UnlinkTest, CannotRemoveDots) { + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const std::string self = JoinPath(file.path(), "."); + ASSERT_THAT(unlink(self.c_str()), SyscallFailsWithErrno(ENOTDIR)); + const std::string parent = JoinPath(file.path(), ".."); + ASSERT_THAT(unlink(parent.c_str()), SyscallFailsWithErrno(ENOTDIR)); +} + +TEST(UnlinkTest, CannotRemoveRoot) { + ASSERT_THAT(unlinkat(-1, "/", AT_REMOVEDIR), SyscallFailsWithErrno(EBUSY)); +} + +TEST(UnlinkTest, CannotRemoveRootWithAtDir) { + const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( + Open(GetAbsoluteTestTmpdir(), O_DIRECTORY, 0666)); + ASSERT_THAT(unlinkat(dirfd.get(), "/", AT_REMOVEDIR), + SyscallFailsWithErrno(EBUSY)); +} + +TEST(RmdirTest, CannotRemoveDots) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string self = JoinPath(dir.path(), "."); + ASSERT_THAT(rmdir(self.c_str()), SyscallFailsWithErrno(EINVAL)); + const std::string parent = JoinPath(dir.path(), ".."); + ASSERT_THAT(rmdir(parent.c_str()), SyscallFailsWithErrno(ENOTEMPTY)); +} + +TEST(RmdirTest, CanRemoveWithTrailingSlashes) { + auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string slash = absl::StrCat(dir1.path(), "/"); + ASSERT_THAT(rmdir(slash.c_str()), SyscallSucceeds()); + auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string slashslash = absl::StrCat(dir2.path(), "//"); + ASSERT_THAT(rmdir(slashslash.c_str()), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/unshare.cc b/test/syscalls/linux/unshare.cc new file mode 100644 index 000000000..9dd6ec4b6 --- /dev/null +++ b/test/syscalls/linux/unshare.cc @@ -0,0 +1,50 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/synchronization/mutex.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(UnshareTest, AllowsZeroFlags) { + ASSERT_THAT(unshare(0), SyscallSucceeds()); +} + +TEST(UnshareTest, ThreadFlagFailsIfMultithreaded) { + absl::Mutex mu; + bool finished = false; + ScopedThread t([&] { + mu.Lock(); + mu.Await(absl::Condition(&finished)); + mu.Unlock(); + }); + ASSERT_THAT(unshare(CLONE_THREAD), SyscallFailsWithErrno(EINVAL)); + mu.Lock(); + finished = true; + mu.Unlock(); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/utimes.cc b/test/syscalls/linux/utimes.cc new file mode 100644 index 000000000..d95ee74ec --- /dev/null +++ b/test/syscalls/linux/utimes.cc @@ -0,0 +1,330 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/fs_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// TODO: utimes(nullptr) does not pick the "now" time in the +// application's time domain, so when asserting that times are within a window, +// we expand the window to allow for differences between the time domains. +constexpr absl::Duration kClockSlack = absl::Milliseconds(100); + +// TimeBoxed runs fn, setting before and after to (coarse realtime) times +// guaranteed* to come before and after fn started and completed, respectively. +// +// fn may be called more than once if the clock is adjusted. +// +// * See the comment on kClockSlack. gVisor breaks this guarantee. +void TimeBoxed(absl::Time* before, absl::Time* after, + std::function const& fn) { + do { + // N.B. utimes and friends use CLOCK_REALTIME_COARSE for setting time (i.e., + // current_kernel_time()). See fs/attr.c:notify_change. + // + // notify_change truncates the time to a multiple of s_time_gran, but most + // filesystems set it to 1, so we don't do any truncation. + struct timespec ts; + EXPECT_THAT(clock_gettime(CLOCK_REALTIME_COARSE, &ts), SyscallSucceeds()); + *before = absl::TimeFromTimespec(ts); + + fn(); + + EXPECT_THAT(clock_gettime(CLOCK_REALTIME_COARSE, &ts), SyscallSucceeds()); + *after = absl::TimeFromTimespec(ts); + + if (*after < *before) { + // Clock jumped backwards; retry. + // + // Technically this misses jumps small enough to keep after > before, + // which could lead to test failures, but that is very unlikely to happen. + continue; + } + + if (IsRunningOnGvisor()) { + // See comment on kClockSlack. + *before -= kClockSlack; + *after += kClockSlack; + } + } while (*after < *before); +} + +void TestUtimesOnPath(std::string const& path) { + struct stat statbuf; + + struct timeval times[2] = {{1, 0}, {2, 0}}; + EXPECT_THAT(utimes(path.c_str(), times), SyscallSucceeds()); + EXPECT_THAT(stat(path.c_str(), &statbuf), SyscallSucceeds()); + EXPECT_EQ(1, statbuf.st_atime); + EXPECT_EQ(2, statbuf.st_mtime); + + absl::Time before; + absl::Time after; + TimeBoxed(&before, &after, [&] { + EXPECT_THAT(utimes(path.c_str(), nullptr), SyscallSucceeds()); + }); + + EXPECT_THAT(stat(path.c_str(), &statbuf), SyscallSucceeds()); + + absl::Time atime = absl::TimeFromTimespec(statbuf.st_atim); + EXPECT_GE(atime, before); + EXPECT_LE(atime, after); + + absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim); + EXPECT_GE(mtime, before); + EXPECT_LE(mtime, after); +} + +TEST(UtimesTest, OnFile) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + TestUtimesOnPath(f.path()); +} + +TEST(UtimesTest, OnDir) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + TestUtimesOnPath(dir.path()); +} + +TEST(UtimesTest, MissingPath) { + auto path = NewTempAbsPath(); + struct timeval times[2] = {{1, 0}, {2, 0}}; + EXPECT_THAT(utimes(path.c_str(), times), SyscallFailsWithErrno(ENOENT)); +} + +void TestFutimesat(int dirFd, std::string const& path) { + struct stat statbuf; + + struct timeval times[2] = {{1, 0}, {2, 0}}; + EXPECT_THAT(futimesat(dirFd, path.c_str(), times), SyscallSucceeds()); + EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds()); + EXPECT_EQ(1, statbuf.st_atime); + EXPECT_EQ(2, statbuf.st_mtime); + + absl::Time before; + absl::Time after; + TimeBoxed(&before, &after, [&] { + EXPECT_THAT(futimesat(dirFd, path.c_str(), nullptr), SyscallSucceeds()); + }); + + EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds()); + + absl::Time atime = absl::TimeFromTimespec(statbuf.st_atim); + EXPECT_GE(atime, before); + EXPECT_LE(atime, after); + + absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim); + EXPECT_GE(mtime, before); + EXPECT_LE(mtime, after); +} + +TEST(FutimesatTest, OnAbsPath) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + TestFutimesat(0, f.path()); +} + +TEST(FutimesatTest, OnRelPath) { + auto d = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(d.path())); + auto basename = std::string(Basename(f.path())); + const FileDescriptor dirFd = + ASSERT_NO_ERRNO_AND_VALUE(Open(d.path(), O_RDONLY | O_DIRECTORY)); + TestFutimesat(dirFd.get(), basename); +} + +TEST(FutimesatTest, InvalidNsec) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + struct timeval times[4][2] = {{ + {0, 1}, // Valid + {1, static_cast(1e7)} // Invalid + }, + { + {1, static_cast(1e7)}, // Invalid + {0, 1} // Valid + }, + { + {0, 1}, // Valid + {1, -1} // Invalid + }, + { + {1, -1}, // Invalid + {0, 1} // Valid + }}; + + for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) { + std::cout << "test:" << i << "\n"; + EXPECT_THAT(futimesat(0, f.path().c_str(), times[i]), + SyscallFailsWithErrno(EINVAL)); + } +} + +void TestUtimensat(int dirFd, std::string const& path) { + struct stat statbuf; + const struct timespec times[2] = {{1, 0}, {2, 0}}; + EXPECT_THAT(utimensat(dirFd, path.c_str(), times, 0), SyscallSucceeds()); + EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds()); + EXPECT_EQ(1, statbuf.st_atime); + EXPECT_EQ(2, statbuf.st_mtime); + + // Test setting with UTIME_NOW and UTIME_OMIT. + struct stat statbuf2; + const struct timespec times2[2] = { + {0, UTIME_NOW}, // Should set atime to now. + {0, UTIME_OMIT} // Should not change mtime. + }; + + absl::Time before; + absl::Time after; + TimeBoxed(&before, &after, [&] { + EXPECT_THAT(utimensat(dirFd, path.c_str(), times2, 0), SyscallSucceeds()); + }); + + EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf2, 0), SyscallSucceeds()); + + absl::Time atime2 = absl::TimeFromTimespec(statbuf2.st_atim); + EXPECT_GE(atime2, before); + EXPECT_LE(atime2, after); + + absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim); + absl::Time mtime2 = absl::TimeFromTimespec(statbuf2.st_mtim); + // mtime should not be changed. + EXPECT_EQ(mtime, mtime2); + + // Test setting with times = NULL. Should set both atime and mtime to the + // current system time. + struct stat statbuf3; + TimeBoxed(&before, &after, [&] { + EXPECT_THAT(utimensat(dirFd, path.c_str(), nullptr, 0), SyscallSucceeds()); + }); + + EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf3, 0), SyscallSucceeds()); + + absl::Time atime3 = absl::TimeFromTimespec(statbuf3.st_atim); + EXPECT_GE(atime3, before); + EXPECT_LE(atime3, after); + + absl::Time mtime3 = absl::TimeFromTimespec(statbuf3.st_mtim); + EXPECT_GE(mtime3, before); + EXPECT_LE(mtime3, after); + + if (!IsRunningOnGvisor()) { + // FIXME: Gofers set atime and mtime to different "now" times. + EXPECT_EQ(atime3, mtime3); + } +} + +TEST(UtimensatTest, OnAbsPath) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + TestUtimensat(0, f.path()); +} + +TEST(UtimensatTest, OnRelPath) { + auto d = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(d.path())); + auto basename = std::string(Basename(f.path())); + const FileDescriptor dirFd = + ASSERT_NO_ERRNO_AND_VALUE(Open(d.path(), O_RDONLY | O_DIRECTORY)); + TestUtimensat(dirFd.get(), basename); +} + +TEST(UtimensatTest, OmitNoop) { + // Setting both timespecs to UTIME_OMIT on a nonexistant path should succeed. + auto path = NewTempAbsPath(); + const struct timespec times[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}}; + EXPECT_THAT(utimensat(0, path.c_str(), times, 0), SyscallSucceeds()); +} + +// Verify that we can actually set atime and mtime to 0. +TEST(UtimeTest, ZeroAtimeandMtime) { + const auto tmp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const auto tmp_file = + ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmp_dir.path())); + + // Stat the file before and after updating atime and mtime. + struct stat stat_before = {}; + EXPECT_THAT(stat(tmp_file.path().c_str(), &stat_before), SyscallSucceeds()); + + ASSERT_NE(stat_before.st_atime, 0); + ASSERT_NE(stat_before.st_mtime, 0); + + const struct utimbuf times = {}; // Zero for both atime and mtime. + EXPECT_THAT(utime(tmp_file.path().c_str(), ×), SyscallSucceeds()); + + struct stat stat_after = {}; + EXPECT_THAT(stat(tmp_file.path().c_str(), &stat_after), SyscallSucceeds()); + + // We should see the atime and mtime changed when we set them to 0. + ASSERT_EQ(stat_after.st_atime, 0); + ASSERT_EQ(stat_after.st_mtime, 0); +} + +TEST(UtimensatTest, InvalidNsec) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + struct timespec times[2][2] = {{ + {0, UTIME_OMIT}, // Valid + {2, static_cast(1e10)} // Invalid + }, + { + {2, static_cast(1e10)}, // Invalid + {0, UTIME_OMIT} // Valid + }}; + + for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) { + std::cout << "test:" << i << "\n"; + EXPECT_THAT(utimensat(0, f.path().c_str(), times[i], 0), + SyscallFailsWithErrno(EINVAL)); + } +} + +TEST(Utimensat, NullPath) { + // From man utimensat(2): + // "the Linux utimensat() system call implements a nonstandard feature: if + // pathname is NULL, then the call modifies the timestamps of the file + // referred to by the file descriptor dirfd (which may refer to any type of + // file). + // Note, however, that the glibc wrapper for utimensat() disallows + // passing NULL as the value for file: the wrapper function returns the error + // EINVAL in this case." + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); + struct stat statbuf; + const struct timespec times[2] = {{1, 0}, {2, 0}}; + // Call syscall directly. + EXPECT_THAT(syscall(SYS_utimensat, fd.get(), NULL, times, 0), + SyscallSucceeds()); + EXPECT_THAT(fstatat(0, f.path().c_str(), &statbuf, 0), SyscallSucceeds()); + EXPECT_EQ(1, statbuf.st_atime); + EXPECT_EQ(2, statbuf.st_mtime); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/vdso.cc b/test/syscalls/linux/vdso.cc new file mode 100644 index 000000000..0f6e1c7c6 --- /dev/null +++ b/test/syscalls/linux/vdso.cc @@ -0,0 +1,48 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/proc_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Ensure that the vvar page cannot be made writable. +TEST(VvarTest, WriteVvar) { + auto contents = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); + auto maps = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(contents)); + auto it = std::find_if(maps.begin(), maps.end(), [](const ProcMapsEntry& e) { + return e.filename == "[vvar]"; + }); + + SKIP_IF(it == maps.end()); + EXPECT_THAT(mprotect(reinterpret_cast(it->start), kPageSize, + PROT_READ | PROT_WRITE), + SyscallFailsWithErrno(EACCES)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/vdso_clock_gettime.cc b/test/syscalls/linux/vdso_clock_gettime.cc new file mode 100644 index 000000000..59dd78833 --- /dev/null +++ b/test/syscalls/linux/vdso_clock_gettime.cc @@ -0,0 +1,104 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/numbers.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +std::string PrintClockId(::testing::TestParamInfo info) { + switch (info.param) { + case CLOCK_MONOTONIC: + return "CLOCK_MONOTONIC"; + case CLOCK_REALTIME: + return "CLOCK_REALTIME"; + default: + return absl::StrCat(info.param); + } +} + +class CorrectVDSOClockTest : public ::testing::TestWithParam {}; + +TEST_P(CorrectVDSOClockTest, IsCorrect) { + struct timespec tvdso, tsys; + absl::Time vdso_time, sys_time; + uint64_t total_calls = 0; + + // It is expected that 82.5% of clock_gettime calls will be less than 100us + // skewed from the system time. + // Unfortunately this is not only influenced by the VDSO clock skew, but also + // by arbitrary scheduling delays and the like. The test is therefore + // regularly disabled. + std::map> confidence = + { + {absl::Microseconds(100), std::make_tuple(0.825, 0, 0)}, + {absl::Microseconds(250), std::make_tuple(0.94, 0, 0)}, + {absl::Milliseconds(1), std::make_tuple(0.999, 0, 0)}, + }; + + absl::Time start = absl::Now(); + while (absl::Now() < start + absl::Seconds(30)) { + EXPECT_THAT(clock_gettime(GetParam(), &tvdso), SyscallSucceeds()); + EXPECT_THAT(syscall(__NR_clock_gettime, GetParam(), &tsys), + SyscallSucceeds()); + + vdso_time = absl::TimeFromTimespec(tvdso); + + for (auto const& conf : confidence) { + std::get<1>(confidence[conf.first]) += + (sys_time - vdso_time) < conf.first; + } + + sys_time = absl::TimeFromTimespec(tsys); + + for (auto const& conf : confidence) { + std::get<2>(confidence[conf.first]) += + (vdso_time - sys_time) < conf.first; + } + + ++total_calls; + } + + for (auto const& conf : confidence) { + EXPECT_GE(std::get<1>(conf.second) / static_cast(total_calls), + std::get<0>(conf.second)); + EXPECT_GE(std::get<2>(conf.second) / static_cast(total_calls), + std::get<0>(conf.second)); + } +} + +INSTANTIATE_TEST_CASE_P(ClockGettime, CorrectVDSOClockTest, + ::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME), + PrintClockId); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/vfork.cc b/test/syscalls/linux/vfork.cc new file mode 100644 index 000000000..9999a909e --- /dev/null +++ b/test/syscalls/linux/vfork.cc @@ -0,0 +1,193 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/time/time.h" +#include "test/util/logging.h" +#include "test/util/multiprocess_util.h" +#include "test/util/test_util.h" + +DEFINE_bool(vfork_test_child, false, + "If true, run the VforkTest child workload."); + +namespace gvisor { +namespace testing { + +namespace { + +// We don't test with raw CLONE_VFORK to avoid interacting with glibc's use of +// TLS. +// +// Even with vfork(2), we must be careful to do little more in the child than +// call execve(2). We use the simplest sleep function possible, though this is +// still precarious, as we're officially only allowed to call execve(2) and +// _exit(2). +constexpr absl::Duration kChildDelay = absl::Seconds(10); + +// Exit code for successful child subprocesses. We don't want to use 0 since +// it's too common, and an execve(2) failure causes the child to exit with the +// errno, so kChildExitCode is chosen to be an unlikely errno: +constexpr int kChildExitCode = 118; // ENOTNAM: Not a XENIX named type file + +int64_t MonotonicNow() { + struct timespec now; + TEST_PCHECK(clock_gettime(CLOCK_MONOTONIC, &now) == 0); + return now.tv_sec * 1000000000ll + now.tv_nsec; +} + +TEST(VforkTest, ParentStopsUntilChildExits) { + const auto test = [] { + // N.B. Run the test in a single-threaded subprocess because + // vfork is not safe in a multi-threaded process. + + const int64_t start = MonotonicNow(); + + pid_t pid = vfork(); + if (pid == 0) { + SleepSafe(kChildDelay); + _exit(kChildExitCode); + } + TEST_PCHECK_MSG(pid > 0, "vfork failed"); + MaybeSave(); + + const int64_t end = MonotonicNow(); + + absl::Duration dur = absl::Nanoseconds(end - start); + + TEST_CHECK(dur >= kChildDelay); + + int status = 0; + TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0)); + TEST_CHECK(WIFEXITED(status)); + TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); + }; + + EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0)); +} + +TEST(VforkTest, ParentStopsUntilChildExecves_NoRandomSave) { + ExecveArray const owned_child_argv = {"/proc/self/exe", "--vfork_test_child"}; + char* const* const child_argv = owned_child_argv.get(); + + const auto test = [&] { + const int64_t start = MonotonicNow(); + + pid_t pid = vfork(); + if (pid == 0) { + SleepSafe(kChildDelay); + execve(child_argv[0], child_argv, /* envp = */ nullptr); + _exit(errno); + } + // Don't attempt save/restore until after recording end_time, + // since the test expects an upper bound on the time spent + // stopped. + int saved_errno = errno; + const int64_t end = MonotonicNow(); + errno = saved_errno; + TEST_PCHECK_MSG(pid > 0, "vfork failed"); + MaybeSave(); + + absl::Duration dur = absl::Nanoseconds(end - start); + + // The parent should resume execution after execve, but before + // the post-execve test child exits. + TEST_CHECK(dur >= kChildDelay); + TEST_CHECK(dur <= 2 * kChildDelay); + + int status = 0; + TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0)); + TEST_CHECK(WIFEXITED(status)); + TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); + }; + + EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0)); +} + +// A vfork child does not unstop the parent a second time when it exits after +// exec. +TEST(VforkTest, ExecedChildExitDoesntUnstopParent_NoRandomSave) { + ExecveArray const owned_child_argv = {"/proc/self/exe", "--vfork_test_child"}; + char* const* const child_argv = owned_child_argv.get(); + + const auto test = [&] { + pid_t pid1 = vfork(); + if (pid1 == 0) { + execve(child_argv[0], child_argv, /* envp = */ nullptr); + _exit(errno); + } + TEST_PCHECK_MSG(pid1 > 0, "vfork failed"); + MaybeSave(); + + // pid1 exec'd and is now sleeping. + SleepSafe(kChildDelay / 2); + + const int64_t start = MonotonicNow(); + + pid_t pid2 = vfork(); + if (pid2 == 0) { + SleepSafe(kChildDelay); + _exit(kChildExitCode); + } + TEST_PCHECK_MSG(pid2 > 0, "vfork failed"); + MaybeSave(); + + const int64_t end = MonotonicNow(); + + absl::Duration dur = absl::Nanoseconds(end - start); + + // The parent should resume execution only after pid2 exits, not + // when pid1 exits. + TEST_CHECK(dur >= kChildDelay); + + int status = 0; + TEST_PCHECK(RetryEINTR(waitpid)(pid1, &status, 0)); + TEST_CHECK(WIFEXITED(status)); + TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); + + TEST_PCHECK(RetryEINTR(waitpid)(pid2, &status, 0)); + TEST_CHECK(WIFEXITED(status)); + TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); + }; + + EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0)); +} + +int RunChild() { + SleepSafe(kChildDelay); + return kChildExitCode; +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + gvisor::testing::TestInit(&argc, &argv); + + if (FLAGS_vfork_test_child) { + return gvisor::testing::RunChild(); + } + + return RUN_ALL_TESTS(); +} diff --git a/test/syscalls/linux/vsyscall.cc b/test/syscalls/linux/vsyscall.cc new file mode 100644 index 000000000..cb6840cc6 --- /dev/null +++ b/test/syscalls/linux/vsyscall.cc @@ -0,0 +1,44 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/proc_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +time_t vsyscall_time(time_t* t) { + constexpr uint64_t kVsyscallTimeEntry = 0xffffffffff600400; + return reinterpret_cast(kVsyscallTimeEntry)(t); +} + +TEST(VsyscallTest, VsyscallAlwaysAvailableOnGvisor) { + SKIP_IF(!IsRunningOnGvisor()); + // Vsyscall is always advertised by gvisor. + EXPECT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); + // Vsyscall should always works on gvisor. + time_t t; + EXPECT_THAT(vsyscall_time(&t), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/wait.cc b/test/syscalls/linux/wait.cc new file mode 100644 index 000000000..0a4ec7c6a --- /dev/null +++ b/test/syscalls/linux/wait.cc @@ -0,0 +1,748 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/cleanup.h" +#include "test/util/logging.h" +#include "test/util/multiprocess_util.h" +#include "test/util/posix_error.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" + +using ::testing::UnorderedElementsAre; + +// These unit tests focus on the wait4(2) system call, but include a basic +// checks for the i386 waitpid(2) syscall, which is a subset of wait4(2). +// +// NOTE: Some functionality is not tested as +// it is not currently supported by gVisor: +// * UID in waitid(2) siginfo. +// * Process groups. +// * Core dump status (WCOREDUMP). +// * Linux only option __WNOTHREAD. +// +// Tests for waiting on stopped/continued children are in sigstop.cc. + +namespace gvisor { +namespace testing { + +namespace { + +// The CloneChild function seems to need more than one page of stack space. +static const size_t kStackSize = 2 * kPageSize; + +// The child thread created in CloneAndExit runs this function. +// This child does not have the TLS setup, so it must not use glibc functions. +int CloneChild(void* priv) { + int64_t sleep = reinterpret_cast(priv); + SleepSafe(absl::Seconds(sleep)); + + // glibc's _exit(2) function wrapper will helpfully call exit_group(2), + // exiting the entire process. + syscall(__NR_exit, 0); + return 1; +} + +// ForkAndExit forks a child process which exits with exit_code, after +// sleeping for the specified duration (seconds). +pid_t ForkAndExit(int exit_code, int64_t sleep) { + pid_t child = fork(); + if (child == 0) { + SleepSafe(absl::Seconds(sleep)); + _exit(exit_code); + } + return child; +} + +int64_t clock_gettime_nsecs(clockid_t id) { + struct timespec ts; + TEST_PCHECK(clock_gettime(id, &ts) == 0); + return (ts.tv_sec * 1000000000 + ts.tv_nsec); +} + +void spin(int64_t sec) { + int64_t ns = sec * 1000000000; + int64_t start = clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID); + int64_t end = start + ns; + + do { + constexpr int kLoopCount = 1000000; // large and arbitrary + // volatile to prevent the compiler from skipping this loop. + for (volatile int i = 0; i < kLoopCount; i++) { + } + } while (clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID) < end); +} + +// ForkSpinAndExit forks a child process which exits with exit_code, after +// spinning for the specified duration (seconds). +pid_t ForkSpinAndExit(int exit_code, int64_t spintime) { + pid_t child = fork(); + if (child == 0) { + spin(spintime); + _exit(exit_code); + } + return child; +} + +absl::Duration RusageCpuTime(const struct rusage& ru) { + return absl::DurationFromTimeval(ru.ru_utime) + + absl::DurationFromTimeval(ru.ru_stime); +} + +// Returns the address of the top of the stack. +// Free with FreeStack. +uintptr_t AllocStack() { + void* addr = mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (addr == MAP_FAILED) { + return reinterpret_cast(MAP_FAILED); + } + + return reinterpret_cast(addr) + kStackSize; +} + +// Frees a stack page allocated with AllocStack. +int FreeStack(uintptr_t addr) { + addr -= kStackSize; + return munmap(reinterpret_cast(addr), kPageSize); +} + +// CloneAndExit clones a child thread, which exits with 0 after sleeping for +// the specified duration (must be in seconds). extra_flags are ORed against +// the standard clone(2) flags. +int CloneAndExit(int64_t sleep, uintptr_t stack, int extra_flags) { + return clone(CloneChild, reinterpret_cast(stack), + CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_VM | extra_flags, + reinterpret_cast(sleep)); +} + +// Simple wrappers around wait4(2) and waitid(2) that ignore interrupts. +constexpr auto Wait4 = RetryEINTR(wait4); +constexpr auto Waitid = RetryEINTR(waitid); + +// Fixture for tests parameterized by a function that waits for any child to +// exit with the given options, checks that it exited with the given code, and +// then returns its PID. +// +// N.B. These tests run in a multi-threaded environment. We assume that +// background threads do not create child processes and are not themselves +// created with clone(... | SIGCHLD). Either may cause these tests to +// erroneously wait on child processes/threads. +class WaitAnyChildTest : public ::testing::TestWithParam< + std::function(int, int)>> { + protected: + PosixErrorOr WaitAny(int code) { return WaitAnyWithOptions(code, 0); } + + PosixErrorOr WaitAnyWithOptions(int code, int options) { + return GetParam()(code, options); + } +}; + +// Wait for any child to exit. +TEST_P(WaitAnyChildTest, Fork) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); + + EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); +} + +// Call wait4 for any process after the child has already exited. +TEST_P(WaitAnyChildTest, AfterExit) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); + + absl::SleepFor(absl::Seconds(5)); + + EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); +} + +// Wait for multiple children to exit, waiting for either at a time. +TEST_P(WaitAnyChildTest, MultipleFork) { + pid_t child1, child2; + ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds()); + ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds()); + + std::vector pids; + pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); + pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); + EXPECT_THAT(pids, UnorderedElementsAre(child1, child2)); +} + +// Wait for any child to exit. +// A non-CLONE_THREAD child which sends SIGCHLD upon exit behaves much like +// a forked process. +TEST_P(WaitAnyChildTest, CloneSIGCHLD) { + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + int child; + ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); + + EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); +} + +// Wait for a child thread and process. +TEST_P(WaitAnyChildTest, ForkAndClone) { + pid_t process; + ASSERT_THAT(process = ForkAndExit(0, 0), SyscallSucceeds()); + + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + int thread; + // Send SIGCHLD for normal wait semantics. + ASSERT_THAT(thread = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); + + std::vector pids; + pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); + pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); + EXPECT_THAT(pids, UnorderedElementsAre(process, thread)); +} + +// Return immediately if no child has exited. +TEST_P(WaitAnyChildTest, WaitWNOHANG) { + EXPECT_THAT( + WaitAnyWithOptions(0, WNOHANG), + PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"), + ::testing::StrEq("wait4")))); +} + +// Bad options passed +TEST_P(WaitAnyChildTest, BadOption) { + EXPECT_THAT( + WaitAnyWithOptions(0, 123456), + PosixErrorIs(EINVAL, ::testing::AnyOf(::testing::StrEq("waitid"), + ::testing::StrEq("wait4")))); +} + +TEST_P(WaitAnyChildTest, WaitedChildRusage) { + struct rusage before; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &before), SyscallSucceeds()); + + pid_t child; + constexpr absl::Duration kSpin = absl::Seconds(3); + ASSERT_THAT(child = ForkSpinAndExit(0, absl::ToInt64Seconds(kSpin)), + SyscallSucceeds()); + ASSERT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); + + struct rusage after; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &after), SyscallSucceeds()); + + EXPECT_GE(RusageCpuTime(after) - RusageCpuTime(before), kSpin); +} + +TEST_P(WaitAnyChildTest, IgnoredChildRusage) { + // "POSIX.1-2001 specifies that if the disposition of SIGCHLD is + // set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see + // sigaction(2)), then children that terminate do not become zombies and a + // call to wait() or waitpid() will block until all children have terminated, + // and then fail with errno set to ECHILD." - waitpid(2) + // + // "RUSAGE_CHILDREN: Return resource usage statistics for all children of the + // calling process that have terminated *and been waited for*." - + // getrusage(2), emphasis added + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + const auto cleanup_sigact = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa)); + + struct rusage before; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &before), SyscallSucceeds()); + + const absl::Duration start = + absl::Nanoseconds(clock_gettime_nsecs(CLOCK_MONOTONIC)); + + constexpr absl::Duration kSpin = absl::Seconds(3); + + // ForkAndSpin uses CLOCK_THREAD_CPUTIME_ID, which is lower resolution than, + // and may diverge from, CLOCK_MONOTONIC, so we allow a small grace period but + // still check that we blocked for a while. + constexpr absl::Duration kSpinGrace = absl::Milliseconds(100); + + pid_t child; + ASSERT_THAT(child = ForkSpinAndExit(0, absl::ToInt64Seconds(kSpin)), + SyscallSucceeds()); + ASSERT_THAT(WaitAny(0), PosixErrorIs(ECHILD, ::testing::AnyOf( + ::testing::StrEq("waitid"), + ::testing::StrEq("wait4")))); + const absl::Duration end = + absl::Nanoseconds(clock_gettime_nsecs(CLOCK_MONOTONIC)); + EXPECT_GE(end - start, kSpin - kSpinGrace); + + struct rusage after; + ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &after), SyscallSucceeds()); + EXPECT_EQ(before.ru_utime.tv_sec, after.ru_utime.tv_sec); + EXPECT_EQ(before.ru_utime.tv_usec, after.ru_utime.tv_usec); + EXPECT_EQ(before.ru_stime.tv_sec, after.ru_stime.tv_sec); + EXPECT_EQ(before.ru_stime.tv_usec, after.ru_stime.tv_usec); +} + +INSTANTIATE_TEST_CASE_P( + Waiters, WaitAnyChildTest, + ::testing::Values( + [](int code, int options) -> PosixErrorOr { + int status; + auto const pid = Wait4(-1, &status, options, nullptr); + MaybeSave(); + if (pid < 0) { + return PosixError(errno, "wait4"); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != code) { + return PosixError( + EINVAL, absl::StrCat("unexpected wait status: got ", status, + ", wanted ", code)); + } + return static_cast(pid); + }, + [](int code, int options) -> PosixErrorOr { + siginfo_t si; + auto const rv = Waitid(P_ALL, 0, &si, WEXITED | options); + MaybeSave(); + if (rv < 0) { + return PosixError(errno, "waitid"); + } + if (si.si_signo != SIGCHLD) { + return PosixError( + EINVAL, absl::StrCat("unexpected signo: got ", si.si_signo, + ", wanted ", SIGCHLD)); + } + if (si.si_status != code) { + return PosixError( + EINVAL, absl::StrCat("unexpected status: got ", si.si_status, + ", wanted ", code)); + } + if (si.si_code != CLD_EXITED) { + return PosixError(EINVAL, + absl::StrCat("unexpected code: got ", si.si_code, + ", wanted ", CLD_EXITED)); + } + auto const uid = getuid(); + if (si.si_uid != uid) { + return PosixError(EINVAL, + absl::StrCat("unexpected uid: got ", si.si_uid, + ", wanted ", uid)); + } + return static_cast(si.si_pid); + })); + +// Fixture for tests parameterized by a function that takes the PID of a +// specific child to wait for, waits for it to exit, and checks that it exits +// with the given code. +class WaitSpecificChildTest + : public ::testing::TestWithParam> { + protected: + PosixError WaitFor(pid_t pid, int code) { return GetParam()(pid, code); } +}; + +// Wait for specific child to exit. +TEST_P(WaitSpecificChildTest, Fork) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); + + EXPECT_NO_ERRNO(WaitFor(child, 0)); +} + +// Non-zero exit codes are correctly propagated. +TEST_P(WaitSpecificChildTest, NormalExit) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); + + EXPECT_NO_ERRNO(WaitFor(child, 42)); +} + +// Wait for multiple children to exit. +TEST_P(WaitSpecificChildTest, MultipleFork) { + pid_t child1, child2; + ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds()); + ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds()); + + EXPECT_NO_ERRNO(WaitFor(child1, 0)); + EXPECT_NO_ERRNO(WaitFor(child2, 0)); +} + +// Wait for multiple children to exit, out of the order they were created. +TEST_P(WaitSpecificChildTest, MultipleForkOutOfOrder) { + pid_t child1, child2; + ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds()); + ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds()); + + EXPECT_NO_ERRNO(WaitFor(child2, 0)); + EXPECT_NO_ERRNO(WaitFor(child1, 0)); +} + +// Wait for specific child to exit, entering wait4 before the exit occurs. +TEST_P(WaitSpecificChildTest, ForkSleep) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(0, 5), SyscallSucceeds()); + + EXPECT_NO_ERRNO(WaitFor(child, 0)); +} + +// Wait should block until the child exits. +TEST_P(WaitSpecificChildTest, ForkBlock) { + pid_t child; + + auto start = absl::Now(); + ASSERT_THAT(child = ForkAndExit(0, 5), SyscallSucceeds()); + + EXPECT_NO_ERRNO(WaitFor(child, 0)); + + EXPECT_GE(absl::Now() - start, absl::Seconds(5)); +} + +// Waiting after the child has already exited returns immediately. +TEST_P(WaitSpecificChildTest, AfterExit) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); + + absl::SleepFor(absl::Seconds(5)); + + EXPECT_NO_ERRNO(WaitFor(child, 0)); +} + +// Wait for specific child to exit. +// A non-CLONE_THREAD child which sends SIGCHLD upon exit behaves much like +// a forked process. +TEST_P(WaitSpecificChildTest, CloneSIGCHLD) { + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + int child; + ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); + + EXPECT_NO_ERRNO(WaitFor(child, 0)); +} + +// Wait for specific child to exit. +// A non-CLONE_THREAD child which does not send SIGCHLD upon exit can be waited +// on, but returns ECHILD. +TEST_P(WaitSpecificChildTest, CloneNoSIGCHLD) { + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + int child; + ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds()); + + EXPECT_THAT( + WaitFor(child, 0), + PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"), + ::testing::StrEq("wait4")))); +} + +// Waiting after the child has already exited returns immediately. +TEST_P(WaitSpecificChildTest, CloneAfterExit) { + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + int child; + // Send SIGCHLD for normal wait semantics. + ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); + + absl::SleepFor(absl::Seconds(5)); + + EXPECT_NO_ERRNO(WaitFor(child, 0)); +} + +// A CLONE_THREAD child cannot be waited on. +TEST_P(WaitSpecificChildTest, CloneThread) { + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + int child; + ASSERT_THAT(child = CloneAndExit(15, stack, CLONE_THREAD), SyscallSucceeds()); + auto start = absl::Now(); + + EXPECT_THAT( + WaitFor(child, 0), + PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"), + ::testing::StrEq("wait4")))); + + // Ensure wait4 didn't block. + EXPECT_LE(absl::Now() - start, absl::Seconds(10)); + + // Since we can't wait on the child, we sleep to try to avoid freeing its + // stack before it exits. + absl::SleepFor(absl::Seconds(5)); +} + +// Return ECHILD for bad child. +TEST_P(WaitSpecificChildTest, BadChild) { + EXPECT_THAT( + WaitFor(42, 0), + PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"), + ::testing::StrEq("wait4")))); +} + +// Wait for a child process that only exits after calling execve(2) from a +// non-leader thread. +TEST_P(WaitSpecificChildTest, AfterChildExecve) { + ExecveArray const owned_child_argv = {"/bin/true"}; + char* const* const child_argv = owned_child_argv.get(); + + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + pid_t const child = fork(); + if (child == 0) { + // Give the parent some time to start waiting. + SleepSafe(absl::Seconds(5)); + // Pass CLONE_VFORK to block the original thread in the child process until + // the clone thread calls execve, annihilating them both. (This means that + // if clone returns at all, something went wrong.) + // + // N.B. clone(2) is not officially async-signal-safe, but at minimum glibc's + // x86_64 implementation is safe. See glibc + // sysdeps/unix/sysv/linux/x86_64/clone.S. + clone( + +[](void* arg) { + auto child_argv = static_cast(arg); + execve(child_argv[0], child_argv, /* envp = */ nullptr); + return errno; + }, + reinterpret_cast(stack), + CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | + CLONE_VFORK, + const_cast(child_argv)); + _exit(errno); + } + EXPECT_NO_ERRNO(WaitFor(child, 0)); +} + +INSTANTIATE_TEST_CASE_P( + Waiters, WaitSpecificChildTest, + ::testing::Values( + [](pid_t pid, int code) -> PosixError { + int status; + auto const rv = Wait4(pid, &status, 0, nullptr); + MaybeSave(); + if (rv < 0) { + return PosixError(errno, "wait4"); + } else if (rv != pid) { + return PosixError(EINVAL, absl::StrCat("unexpected pid: got ", rv, + ", wanted ", pid)); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != code) { + return PosixError( + EINVAL, absl::StrCat("unexpected wait status: got ", status, + ", wanted ", code)); + } + return NoError(); + }, + [](pid_t pid, int code) -> PosixError { + siginfo_t si; + auto const rv = Waitid(P_PID, pid, &si, WEXITED); + MaybeSave(); + if (rv < 0) { + return PosixError(errno, "waitid"); + } + if (si.si_pid != pid) { + return PosixError(EINVAL, + absl::StrCat("unexpected pid: got ", si.si_pid, + ", wanted ", pid)); + } + if (si.si_signo != SIGCHLD) { + return PosixError( + EINVAL, absl::StrCat("unexpected signo: got ", si.si_signo, + ", wanted ", SIGCHLD)); + } + if (si.si_status != code) { + return PosixError( + EINVAL, absl::StrCat("unexpected status: got ", si.si_status, + ", wanted ", code)); + } + if (si.si_code != CLD_EXITED) { + return PosixError(EINVAL, + absl::StrCat("unexpected code: got ", si.si_code, + ", wanted ", CLD_EXITED)); + } + return NoError(); + })); + +// WIFEXITED, WIFSIGNALED, WTERMSIG indicate signal exit. +TEST(WaitTest, SignalExit) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(0, 10), SyscallSucceeds()); + + EXPECT_THAT(kill(child, SIGKILL), SyscallSucceeds()); + + int status; + EXPECT_THAT(Wait4(child, &status, 0, nullptr), + SyscallSucceedsWithValue(child)); + + EXPECT_FALSE(WIFEXITED(status)); + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(SIGKILL, WTERMSIG(status)); +} + +// A child that does not send a SIGCHLD on exit may be waited on with +// the __WCLONE flag. +TEST(WaitTest, CloneWCLONE) { + uintptr_t stack; + ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); + auto free = Cleanup( + [this, stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); + + int child; + ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds()); + + EXPECT_THAT(Wait4(child, nullptr, __WCLONE, nullptr), + SyscallSucceedsWithValue(child)); +} + +// waitid requires at least one option. +TEST(WaitTest, WaitidOptions) { + EXPECT_THAT(Waitid(P_ALL, 0, nullptr, 0), SyscallFailsWithErrno(EINVAL)); +} + +// waitid does not wait for a child to exit if not passed WEXITED. +TEST(WaitTest, WaitidNoWEXITED) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); + EXPECT_THAT(Waitid(P_ALL, 0, nullptr, WSTOPPED), + SyscallFailsWithErrno(ECHILD)); + EXPECT_THAT(Waitid(P_ALL, 0, nullptr, WEXITED), SyscallSucceeds()); +} + +// WNOWAIT allows the same wait result to be returned again. +TEST(WaitTest, WaitidWNOWAIT) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); + + siginfo_t info; + ASSERT_THAT(Waitid(P_PID, child, &info, WEXITED | WNOWAIT), + SyscallSucceeds()); + EXPECT_EQ(child, info.si_pid); + EXPECT_EQ(SIGCHLD, info.si_signo); + EXPECT_EQ(CLD_EXITED, info.si_code); + EXPECT_EQ(42, info.si_status); + + ASSERT_THAT(Waitid(P_PID, child, &info, WEXITED), SyscallSucceeds()); + EXPECT_EQ(child, info.si_pid); + EXPECT_EQ(SIGCHLD, info.si_signo); + EXPECT_EQ(CLD_EXITED, info.si_code); + EXPECT_EQ(42, info.si_status); + + EXPECT_THAT(Waitid(P_PID, child, &info, WEXITED), + SyscallFailsWithErrno(ECHILD)); +} + +// waitpid(pid, status, options) is equivalent to +// wait4(pid, status, options, nullptr). +// This is a dedicated syscall on i386, glibc maps it to wait4 on amd64. +TEST(WaitTest, WaitPid) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); + + int status; + EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), + SyscallSucceedsWithValue(child)); + + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(42, WEXITSTATUS(status)); +} + +// Test that signaling a zombie succeeds. This is a signals test that is in this +// file for some reason. +TEST(WaitTest, KillZombie) { + pid_t child; + ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); + + // Sleep for three seconds to ensure the child has exited. + absl::SleepFor(absl::Seconds(3)); + + // The child is now a zombie. Check that killing it returns 0. + EXPECT_THAT(kill(child, SIGTERM), SyscallSucceeds()); + EXPECT_THAT(kill(child, 0), SyscallSucceeds()); + + EXPECT_THAT(Wait4(child, nullptr, 0, nullptr), + SyscallSucceedsWithValue(child)); +} + +TEST(WaitTest, Wait4Rusage) { + pid_t child; + constexpr absl::Duration kSpin = absl::Seconds(3); + ASSERT_THAT(child = ForkSpinAndExit(21, absl::ToInt64Seconds(kSpin)), + SyscallSucceeds()); + + int status; + struct rusage rusage = {}; + ASSERT_THAT(Wait4(child, &status, 0, &rusage), + SyscallSucceedsWithValue(child)); + + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(21, WEXITSTATUS(status)); + + EXPECT_GE(RusageCpuTime(rusage), kSpin); +} + +TEST(WaitTest, WaitidRusage) { + pid_t child; + constexpr absl::Duration kSpin = absl::Seconds(3); + ASSERT_THAT(child = ForkSpinAndExit(27, absl::ToInt64Seconds(kSpin)), + SyscallSucceeds()); + + siginfo_t si = {}; + struct rusage rusage = {}; + + // From waitid(2): + // The raw waitid() system call takes a fifth argument, of type + // struct rusage *. If this argument is non-NULL, then it is used + // to return resource usage information about the child, in the + // same manner as wait4(2). + EXPECT_THAT( + RetryEINTR(syscall)(SYS_waitid, P_PID, child, &si, WEXITED, &rusage), + SyscallSucceeds()); + EXPECT_EQ(si.si_signo, SIGCHLD); + EXPECT_EQ(si.si_code, CLD_EXITED); + EXPECT_EQ(si.si_status, 27); + EXPECT_EQ(si.si_pid, child); + + EXPECT_GE(RusageCpuTime(rusage), kSpin); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/write.cc b/test/syscalls/linux/write.cc new file mode 100644 index 000000000..ca6aafd18 --- /dev/null +++ b/test/syscalls/linux/write.cc @@ -0,0 +1,134 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/cleanup.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { +// This test is currently very rudimentary. +// +// TODO: +// * bad buffer states (EFAULT). +// * bad fds (wrong permission, wrong type of file, EBADF). +// * check offset is incremented. +// * check for EOF. +// * writing to pipes, symlinks, special files. +class WriteTest : public ::testing::Test { + public: + ssize_t WriteBytes(int fd, int bytes) { + std::vector buf(bytes); + std::fill(buf.begin(), buf.end(), 'a'); + return WriteFd(fd, buf.data(), buf.size()); + } +}; + +TEST_F(WriteTest, WriteNoExceedsRLimit) { + // Get the current rlimit and restore after test run. + struct rlimit initial_lim; + ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + auto cleanup = Cleanup([&initial_lim] { + EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + }); + + int fd; + struct rlimit setlim; + const int target_lim = 1024; + setlim.rlim_cur = target_lim; + setlim.rlim_max = RLIM_INFINITY; + const std::string pathname = NewTempAbsPath(); + ASSERT_THAT(fd = open(pathname.c_str(), O_WRONLY | O_CREAT, S_IRWXU), + SyscallSucceeds()); + ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); + + EXPECT_THAT(WriteBytes(fd, target_lim), SyscallSucceedsWithValue(target_lim)); + + std::vector buf(target_lim + 1); + std::fill(buf.begin(), buf.end(), 'a'); + EXPECT_THAT(pwrite(fd, buf.data(), target_lim, 1), SyscallSucceeds()); + EXPECT_THAT(pwrite64(fd, buf.data(), target_lim, 1), SyscallSucceeds()); + + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +TEST_F(WriteTest, WriteExceedsRLimit) { + // Get the current rlimit and restore after test run. + struct rlimit initial_lim; + ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + auto cleanup = Cleanup([&initial_lim] { + EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); + }); + + int fd; + sigset_t filesize_mask; + sigemptyset(&filesize_mask); + sigaddset(&filesize_mask, SIGXFSZ); + + struct rlimit setlim; + const int target_lim = 1024; + setlim.rlim_cur = target_lim; + setlim.rlim_max = RLIM_INFINITY; + + const std::string pathname = NewTempAbsPath(); + ASSERT_THAT(fd = open(pathname.c_str(), O_WRONLY | O_CREAT, S_IRWXU), + SyscallSucceeds()); + ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); + ASSERT_THAT(sigprocmask(SIG_BLOCK, &filesize_mask, nullptr), + SyscallSucceeds()); + std::vector buf(target_lim + 2); + std::fill(buf.begin(), buf.end(), 'a'); + + EXPECT_THAT(write(fd, buf.data(), target_lim + 1), + SyscallSucceedsWithValue(target_lim)); + EXPECT_THAT(write(fd, buf.data(), 1), SyscallFailsWithErrno(EFBIG)); + struct timespec timelimit = {0, 0}; + EXPECT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, nullptr, &timelimit), + SyscallSucceedsWithValue(SIGXFSZ)); + + EXPECT_THAT(pwrite(fd, buf.data(), target_lim + 1, 1), + SyscallSucceedsWithValue(target_lim - 1)); + EXPECT_THAT(pwrite(fd, buf.data(), 1, target_lim), + SyscallFailsWithErrno(EFBIG)); + EXPECT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, nullptr, &timelimit), + SyscallSucceedsWithValue(SIGXFSZ)); + + EXPECT_THAT(pwrite64(fd, buf.data(), target_lim + 1, 1), + SyscallSucceedsWithValue(target_lim - 1)); + EXPECT_THAT(pwrite64(fd, buf.data(), 1, target_lim), + SyscallFailsWithErrno(EFBIG)); + EXPECT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, nullptr, &timelimit), + SyscallSucceedsWithValue(SIGXFSZ)); + + ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &filesize_mask, nullptr), + SyscallSucceeds()); + EXPECT_THAT(close(fd), SyscallSucceeds()); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/syscall_test.go b/test/syscalls/syscall_test.go new file mode 100644 index 000000000..8463289fe --- /dev/null +++ b/test/syscalls/syscall_test.go @@ -0,0 +1,245 @@ +// Copyright 2018 Google LLC +// +// 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 syscall_test runs the syscall test suites in gVisor containers. It +// is meant to be run with "go test", and will panic if run on its own. +package syscall_test + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" + "testing" + + "golang.org/x/sys/unix" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/container" + "gvisor.googlesource.com/gvisor/runsc/specutils" + "gvisor.googlesource.com/gvisor/runsc/test/testutil" + "gvisor.googlesource.com/gvisor/test/syscalls/gtest" +) + +// Location of syscall tests, relative to the repo root. +const testDir = "test/syscalls/linux" + +var ( + testName = flag.String("test-name", "", "name of test binary to run") + debug = flag.Bool("debug", false, "enable debug logs") + strace = flag.Bool("strace", false, "enable strace logs") + platform = flag.String("platform", "ptrace", "platform to run on") + parallel = flag.Bool("parallel", false, "run tests in parallel") +) + +func TestSyscalls(t *testing.T) { + if *testName == "" { + t.Fatalf("test-name flag must be provided") + } + + // Get path to test binary. + fullTestName := filepath.Join(testDir, *testName) + testBin, err := testutil.FindFile(fullTestName) + if err != nil { + t.Fatalf("FindFile(%q) failed: %v", fullTestName, err) + } + + // Get all test cases in each binary. + testCases, err := gtest.ParseTestCases(testBin) + if err != nil { + t.Fatalf("ParseTestCases(%q) failed: %v", testBin, err) + } + + // Make sure stdout and stderr are opened with O_APPEND, otherwise logs + // from outside the sandbox can (and will) stomp on logs from inside + // the sandbox. + for _, f := range []*os.File{os.Stdout, os.Stderr} { + flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0) + if err != nil { + t.Fatalf("error getting file flags for %v: %v", f, err) + } + if flags&unix.O_APPEND == 0 { + flags |= unix.O_APPEND + if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil { + t.Fatalf("error setting file flags for %v: %v", f, err) + } + } + } + + for _, tc := range testCases { + // Capture tc. + tc := tc + + testName := fmt.Sprintf("%s_%s", tc.Suite, tc.Name) + t.Run(testName, func(t *testing.T) { + if *parallel { + t.Parallel() + } + + if *platform == "native" { + // Run the test case on host. + runTestCaseNative(testBin, tc, t) + return + } + + // Run the test case in runsc. + runTestCaseRunsc(testBin, tc, t) + }) + } +} + +// runTestCaseNative runs the test case directly on the host machine. +func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) { + // These tests might be running in parallel, so make sure they have a + // unique test temp dir. + tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "") + if err != nil { + t.Fatalf("could not create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + // Replace TEST_TMPDIR in the current environment with something + // unique. + env := os.Environ() + newEnvVar := "TEST_TMPDIR=" + tmpDir + var found bool + for i, kv := range env { + if strings.HasPrefix(kv, "TEST_TMPDIR=") { + env[i] = newEnvVar + found = true + break + } + } + if !found { + env = append(env, newEnvVar) + } + // Remove the TEST_PREMATURE_EXIT_FILE variable and XML_OUTPUT_FILE + // from the environment. + env = filterEnv(env, []string{"TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"}) + + cmd := exec.Command(testBin, gtest.FilterTestFlag+"="+tc.FullName()) + cmd.Env = env + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + ws := err.(*exec.ExitError).Sys().(syscall.WaitStatus) + t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus()) + } +} + +// runsTestCaseRunsc runs the test case in runsc. +func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) { + rootDir, err := testutil.SetupRootDir() + if err != nil { + t.Fatalf("SetupRootDir failed: %v", err) + } + defer os.RemoveAll(rootDir) + + conf := testutil.TestConfig() + conf.RootDir = rootDir + conf.Debug = *debug + conf.Strace = *strace + p, err := boot.MakePlatformType(*platform) + if err != nil { + t.Fatalf("error getting platform %q: %v", *platform, err) + } + conf.Platform = p + + // Run a new container with the test executable and filter for the + // given test suite and name. + spec := testutil.NewSpecWithArgs(testBin, gtest.FilterTestFlag+"="+tc.FullName()) + + // Mark the root as writeable, as some tests attempt to + // write to the rootfs, and expect EACCES, not EROFS. + spec.Root.Readonly = false + + // Set environment variable that indicates we are + // running in gVisor and with the given platform. + platformVar := "TEST_ON_GVISOR" + env := append(os.Environ(), platformVar+"="+*platform) + + // Remove the TEST_PREMATURE_EXIT_FILE variable and XML_OUTPUT_FILE + // from the environment. + env = filterEnv(env, []string{"TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"}) + + // Set TEST_TMPDIR to /tmp, as some of the syscall tests require it to + // be backed by tmpfs. + for i, kv := range env { + if strings.HasPrefix(kv, "TEST_TMPDIR=") { + env[i] = "TEST_TMPDIR=/tmp" + break + } + } + + spec.Process.Env = env + + bundleDir, err := testutil.SetupBundleDir(spec) + if err != nil { + t.Fatalf("SetupBundleDir failed: %v", err) + } + defer os.RemoveAll(bundleDir) + + id := testutil.UniqueContainerID() + log.Infof("Running test %q in container %q", tc.FullName(), id) + specutils.LogSpec(spec) + ws, err := container.Run(id, spec, conf, bundleDir, "", "", "") + if err != nil { + t.Fatalf("container.Run failed: %v", err) + } + if got := ws.ExitStatus(); got != 0 { + t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus()) + } +} + +// filterEnv returns an environment with the blacklisted variables removed. +func filterEnv(env, blacklist []string) []string { + var out []string + for _, kv := range env { + ok := true + for _, k := range blacklist { + if strings.HasPrefix(kv, k+"=") { + ok = false + break + } + } + if ok { + out = append(out, kv) + } + } + return out +} + +func TestMain(m *testing.M) { + flag.Parse() + + log.SetLevel(log.Warning) + if *debug { + log.SetLevel(log.Debug) + } + if err := testutil.ConfigureExePath(); err != nil { + panic(err.Error()) + } + + if *platform != "native" { + // The native tests don't expect to be running as root, but + // runsc requires it. + testutil.RunAsRoot() + } + + os.Exit(m.Run()) +} diff --git a/test/syscalls/syscall_test_runner.sh b/test/syscalls/syscall_test_runner.sh new file mode 100755 index 000000000..de479f68c --- /dev/null +++ b/test/syscalls/syscall_test_runner.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright 2018 Google LLC +# +# 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. + +# syscall_test_runner.sh is a simple wrapper around the go syscall test runner. +# It exists so that we can build the syscall test runner once, and use it for +# all syscall tests, rather than build it for each test run. + +set -euf -o pipefail + +# The syscall test runner binary and arguments have all been passed as arguments +# to this shell script. +exec "$@" diff --git a/test/util/BUILD b/test/util/BUILD new file mode 100644 index 000000000..e4eec4ab9 --- /dev/null +++ b/test/util/BUILD @@ -0,0 +1,239 @@ +package( + default_visibility = ["//:sandbox"], + licenses = ["notice"], # Apache 2.0 +) + +cc_library( + name = "capability_util", + testonly = 1, + srcs = ["capability_util.cc"], + hdrs = ["capability_util.h"], + deps = [ + ":cleanup", + ":memory_util", + ":posix_error", + ":save_util", + ":test_util", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "file_descriptor", + testonly = 1, + hdrs = ["file_descriptor.h"], + deps = [ + ":logging", + ":posix_error", + ":save_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "proc_util", + testonly = 1, + srcs = ["proc_util.cc"], + hdrs = ["proc_util.h"], + deps = [ + ":fs_util", + ":posix_error", + ":test_util", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "cleanup", + testonly = 1, + hdrs = ["cleanup.h"], +) + +cc_library( + name = "fs_util", + testonly = 1, + srcs = ["fs_util.cc"], + hdrs = ["fs_util.h"], + deps = [ + ":cleanup", + ":file_descriptor", + ":posix_error", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_test( + name = "fs_util_test", + size = "small", + srcs = ["fs_util_test.cc"], + deps = [ + ":fs_util", + ":posix_error", + ":temp_path", + ":test_util", + ], +) + +cc_library( + name = "logging", + testonly = 1, + srcs = ["logging.cc"], + hdrs = ["logging.h"], +) + +cc_library( + name = "memory_util", + testonly = 1, + hdrs = ["memory_util.h"], + deps = [ + ":logging", + ":posix_error", + ":save_util", + ":test_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + ], +) + +cc_library( + name = "mount_util", + testonly = 1, + hdrs = ["mount_util.h"], + deps = [ + ":cleanup", + ":posix_error", + ":test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "save_util", + testonly = 1, + srcs = ["save_util.cc"], + hdrs = ["save_util.h"], +) + +cc_library( + name = "multiprocess_util", + testonly = 1, + srcs = ["multiprocess_util.cc"], + hdrs = ["multiprocess_util.h"], + deps = [ + ":cleanup", + ":file_descriptor", + ":posix_error", + ":save_util", + ":test_util", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "posix_error", + testonly = 1, + srcs = ["posix_error.cc"], + hdrs = ["posix_error.h"], + deps = [ + ":logging", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:variant", + "@com_google_googletest//:gtest", + ], +) + +cc_test( + name = "posix_error_test", + size = "small", + srcs = ["posix_error_test.cc"], + deps = [":posix_error"], +) + +cc_library( + name = "signal_util", + testonly = 1, + srcs = ["signal_util.cc"], + hdrs = ["signal_util.h"], + deps = [ + ":cleanup", + ":posix_error", + ":test_util", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "temp_path", + testonly = 1, + srcs = ["temp_path.cc"], + hdrs = ["temp_path.h"], + deps = [ + ":fs_util", + ":posix_error", + ":test_util", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "test_util", + testonly = 1, + srcs = ["test_util.cc"], + hdrs = ["test_util.h"], + deps = [ + ":fs_util", + ":logging", + ":posix_error", + ":save_util", + "@com_github_gflags_gflags//:gflags", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/time", + "@com_google_glog//:glog", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "thread_util", + testonly = 1, + hdrs = ["thread_util.h"], + deps = [":logging"], +) + +cc_library( + name = "timer_util", + testonly = 1, + srcs = ["timer_util.cc"], + hdrs = ["timer_util.h"], + deps = [ + ":cleanup", + ":logging", + ":posix_error", + ":test_util", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_test( + name = "test_util_test", + size = "small", + srcs = ["test_util_test.cc"], + deps = [":test_util"], +) + +cc_library( + name = "test_main", + testonly = 1, + srcs = ["test_main.cc"], + deps = [":test_util"], +) diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc new file mode 100644 index 000000000..0656775d6 --- /dev/null +++ b/test/util/capability_util.cc @@ -0,0 +1,79 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/capability_util.h" + +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +PosixErrorOr CanCreateUserNamespace() { + // The most reliable way to determine if userns creation is possible is by + // trying to create one; see below. + ASSIGN_OR_RETURN_ERRNO( + auto child_stack, + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + int const child_pid = + clone(+[](void*) { return 0; }, + reinterpret_cast(child_stack.addr() + kPageSize), + CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr); + if (child_pid > 0) { + int status; + int const ret = waitpid(child_pid, &status, /* options = */ 0); + MaybeSave(); + if (ret < 0) { + return PosixError(errno, "waitpid"); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + return PosixError( + ESRCH, absl::StrCat("child process exited with status ", status)); + } + return true; + } else if (errno == EPERM) { + // Per clone(2), EPERM can be returned if: + // + // - "CLONE_NEWUSER was specified in flags, but either the effective user ID + // or the effective group ID of the caller does not have a mapping in the + // parent namespace (see user_namespaces(7))." + // + // - "(since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller + // is in a chroot environment (i.e., the caller's root directory does + // not match the root directory of the mount namespace in which it + // resides)." + LOG(INFO) << "clone(CLONE_NEWUSER) failed with EPERM"; + return false; + } else if (errno == EUSERS) { + // "(since Linux 3.11) CLONE_NEWUSER was specified in flags, and the call + // would cause the limit on the number of nested user namespaces to be + // exceeded. See user_namespaces(7)." + LOG(INFO) << "clone(CLONE_NEWUSER) failed with EUSERS"; + return false; + } else { + // Unexpected error code; indicate an actual error. + return PosixError(errno, "clone(CLONE_NEWUSER)"); + } +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/capability_util.h b/test/util/capability_util.h new file mode 100644 index 000000000..8708f5e69 --- /dev/null +++ b/test/util/capability_util.h @@ -0,0 +1,101 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Utilities for testing capabilties. + +#ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ +#define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ + +#include +#include +#include +#include + +#include "test/util/cleanup.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" +#include "test/util/test_util.h" + +#ifndef _LINUX_CAPABILITY_VERSION_3 +#error Expecting _LINUX_CAPABILITY_VERSION_3 support +#endif + +namespace gvisor { +namespace testing { + +// HaveCapability returns true if the process has the specified EFFECTIVE +// capability. +inline PosixErrorOr HaveCapability(int cap) { + if (!cap_valid(cap)) { + return PosixError(EINVAL, "Invalid capability"); + } + + struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); + MaybeSave(); + + return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0; +} + +// SetCapability sets the specified EFFECTIVE capability. +inline PosixError SetCapability(int cap, bool set) { + if (!cap_valid(cap)) { + return PosixError(EINVAL, "Invalid capability"); + } + + struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); + MaybeSave(); + + if (set) { + caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap); + } else { + caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); + } + header = {_LINUX_CAPABILITY_VERSION_3, 0}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); + MaybeSave(); + + return NoError(); +} + +// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE +// capabilities must be a subset of PERMITTED, so those are dropped as well. +inline PosixError DropPermittedCapability(int cap) { + if (!cap_valid(cap)) { + return PosixError(EINVAL, "Invalid capability"); + } + + struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); + MaybeSave(); + + caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); + caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap); + + header = {_LINUX_CAPABILITY_VERSION_3, 0}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); + MaybeSave(); + + return NoError(); +} + +PosixErrorOr CanCreateUserNamespace(); + +} // namespace testing +} // namespace gvisor +#endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ diff --git a/test/util/cleanup.h b/test/util/cleanup.h new file mode 100644 index 000000000..fb4724f97 --- /dev/null +++ b/test/util/cleanup.h @@ -0,0 +1,61 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_CLEANUP_H_ +#define GVISOR_TEST_UTIL_CLEANUP_H_ + +#include +#include + +namespace gvisor { +namespace testing { + +class Cleanup { + public: + Cleanup() : released_(true) {} + explicit Cleanup(std::function&& callback) : cb_(callback) {} + + Cleanup(Cleanup&& other) { + released_ = other.released_; + cb_ = other.Release(); + } + + Cleanup& operator=(Cleanup&& other) { + released_ = other.released_; + cb_ = other.Release(); + return *this; + } + + ~Cleanup() { + if (!released_) { + cb_(); + } + } + + std::function&& Release() { + released_ = true; + return std::move(cb_); + } + + private: + Cleanup(Cleanup const& other) = delete; + + bool released_ = false; + std::function cb_; +}; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_CLEANUP_H_ diff --git a/test/util/file_descriptor.h b/test/util/file_descriptor.h new file mode 100644 index 000000000..be8812d01 --- /dev/null +++ b/test/util/file_descriptor.h @@ -0,0 +1,134 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ +#define GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ + +#include +#include +#include +#include + +#include +#include + +#include "gmock/gmock.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "test/util/logging.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" + +namespace gvisor { +namespace testing { + +// FileDescriptor is an RAII type class which takes ownership of a file +// descriptor. It will close the FD when this object goes out of scope. +class FileDescriptor { + public: + // Constructs an empty FileDescriptor (one that does not own a file + // descriptor). + FileDescriptor() = default; + + // Constructs a FileDescriptor that owns fd. If fd is negative, constructs an + // empty FileDescriptor. + explicit FileDescriptor(int fd) { set_fd(fd); } + + FileDescriptor(FileDescriptor&& orig) : fd_(orig.release()) {} + + FileDescriptor& operator=(FileDescriptor&& orig) { + reset(orig.release()); + return *this; + } + + PosixErrorOr Dup() const { + if (fd_ < 0) { + return PosixError(EINVAL, "Attempting to Dup unset fd"); + } + + int fd = dup(fd_); + if (fd < 0) { + return PosixError(errno, absl::StrCat("dup ", fd_)); + } + MaybeSave(); + return FileDescriptor(fd); + } + + FileDescriptor(FileDescriptor const& other) = delete; + FileDescriptor& operator=(FileDescriptor const& other) = delete; + + ~FileDescriptor() { reset(); } + + // If this object is non-empty, returns the owned file descriptor. (Ownership + // is retained by the FileDescriptor.) Otherwise returns -1. + int get() const { return fd_; } + + // If this object is non-empty, transfers ownership of the file descriptor to + // the caller and returns it. Otherwise returns -1. + int release() { + int const fd = fd_; + fd_ = -1; + return fd; + } + + // If this object is non-empty, closes the owned file descriptor (recording a + // test failure if the close fails). + void reset() { reset(-1); } + + // Like no-arg reset(), but the FileDescriptor takes ownership of fd after + // closing its existing file descriptor. + void reset(int fd) { + if (fd_ >= 0) { + TEST_PCHECK(close(fd_) == 0); + MaybeSave(); + } + set_fd(fd); + } + + private: + // Wrapper that coerces negative fd values other than -1 to -1 so that get() + // etc. return -1. + void set_fd(int fd) { fd_ = std::max(fd, -1); } + + int fd_ = -1; +}; + +// Wrapper around open(2) that returns a FileDescriptor. +inline PosixErrorOr Open(std::string const& path, int flags, + mode_t mode = 0) { + int fd = open(path.c_str(), flags, mode); + if (fd < 0) { + return PosixError(errno, absl::StrFormat("open(%s, %#x, %#o)", path.c_str(), + flags, mode)); + } + MaybeSave(); + return FileDescriptor(fd); +} + +// Wrapper around openat(2) that returns a FileDescriptor. +inline PosixErrorOr OpenAt(int dirfd, std::string const& path, + int flags, mode_t mode = 0) { + int fd = openat(dirfd, path.c_str(), flags, mode); + if (fd < 0) { + return PosixError(errno, absl::StrFormat("openat(%d, %s, %#x, %#o)", dirfd, + path, flags, mode)); + } + MaybeSave(); + return FileDescriptor(fd); +} + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc new file mode 100644 index 000000000..e7e8be1d8 --- /dev/null +++ b/test/util/fs_util.cc @@ -0,0 +1,585 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/fs_util.h" + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { + +namespace { +PosixError WriteContentsToFD(int fd, absl::string_view contents) { + int written = 0; + while (static_cast(written) < contents.size()) { + int wrote = write(fd, contents.data() + written, contents.size() - written); + if (wrote < 0) { + if (errno == EINTR) { + continue; + } + return PosixError( + errno, absl::StrCat("WriteContentsToFD fd: ", fd, " write failure.")); + } + written += wrote; + } + return NoError(); +} +} // namespace + +namespace internal { + +// Given a collection of file paths, append them all together, +// ensuring that the proper path separators are inserted between them. +std::string JoinPathImpl(std::initializer_list paths) { + std::string result; + + if (paths.size() != 0) { + // This size calculation is worst-case: it assumes one extra "/" for every + // path other than the first. + size_t total_size = paths.size() - 1; + for (const absl::string_view path : paths) total_size += path.size(); + result.resize(total_size); + + auto begin = result.begin(); + auto out = begin; + bool trailing_slash = false; + for (absl::string_view path : paths) { + if (path.empty()) continue; + if (path.front() == '/') { + if (trailing_slash) { + path.remove_prefix(1); + } + } else { + if (!trailing_slash && out != begin) *out++ = '/'; + } + const size_t this_size = path.size(); + memcpy(&*out, path.data(), this_size); + out += this_size; + trailing_slash = out[-1] == '/'; + } + result.erase(out - begin); + } + return result; +} +} // namespace internal + +// Returns a status or the current working directory. +PosixErrorOr GetCWD() { + char buffer[PATH_MAX + 1] = {}; + if (getcwd(buffer, PATH_MAX) == nullptr) { + return PosixError(errno, "GetCWD() failed"); + } + + return std::string(buffer); +} + +PosixErrorOr Stat(absl::string_view path) { + struct stat stat_buf; + int res = stat(std::string(path).c_str(), &stat_buf); + if (res < 0) { + return PosixError(errno, absl::StrCat("stat ", path)); + } + return stat_buf; +} + +PosixErrorOr Exists(absl::string_view path) { + struct stat stat_buf; + int res = stat(std::string(path).c_str(), &stat_buf); + if (res < 0) { + if (errno == ENOENT) { + return false; + } + return PosixError(errno, absl::StrCat("stat ", path)); + } + return true; +} + +PosixErrorOr IsDirectory(absl::string_view path) { + ASSIGN_OR_RETURN_ERRNO(struct stat stat_buf, Stat(path)); + if (S_ISDIR(stat_buf.st_mode)) { + return true; + } + + return false; +} + +PosixError Delete(absl::string_view path) { + int res = unlink(std::string(path).c_str()); + if (res < 0) { + return PosixError(errno, absl::StrCat("unlink ", path)); + } + + return NoError(); +} + +PosixError Truncate(absl::string_view path, int length) { + int res = truncate(std::string(path).c_str(), length); + if (res < 0) { + return PosixError(errno, + absl::StrCat("truncate ", path, " to length ", length)); + } + + return NoError(); +} + +PosixError Chmod(absl::string_view path, int mode) { + int res = chmod(std::string(path).c_str(), mode); + if (res < 0) { + return PosixError(errno, absl::StrCat("chmod ", path)); + } + + return NoError(); +} + +PosixError Mkdir(absl::string_view path, int mode) { + int res = mkdir(std::string(path).c_str(), mode); + if (res < 0) { + return PosixError(errno, absl::StrCat("mkdir ", path, " mode ", mode)); + } + + return NoError(); +} + +PosixError Rmdir(absl::string_view path) { + int res = rmdir(std::string(path).c_str()); + if (res < 0) { + return PosixError(errno, absl::StrCat("rmdir ", path)); + } + + return NoError(); +} + +PosixError SetContents(absl::string_view path, absl::string_view contents) { + ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); + if (!exists) { + return PosixError( + ENOENT, absl::StrCat("SetContents file ", path, " doesn't exist.")); + } + + ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_WRONLY | O_TRUNC)); + return WriteContentsToFD(fd.get(), contents); +} + +// Create a file with the given contents (if it does not already exist with the +// given mode) and then set the contents. +PosixError CreateWithContents(absl::string_view path, + absl::string_view contents, int mode) { + ASSIGN_OR_RETURN_ERRNO( + auto fd, Open(std::string(path), O_WRONLY | O_CREAT | O_TRUNC, mode)); + return WriteContentsToFD(fd.get(), contents); +} + +PosixError GetContents(absl::string_view path, std::string* output) { + ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_RDONLY)); + output->clear(); + + // Keep reading until we hit an EOF or an error. + return GetContentsFD(fd.get(), output); +} + +PosixErrorOr GetContents(absl::string_view path) { + std::string ret; + RETURN_IF_ERRNO(GetContents(path, &ret)); + return ret; +} + +PosixErrorOr GetContentsFD(int fd) { + std::string ret; + RETURN_IF_ERRNO(GetContentsFD(fd, &ret)); + return ret; +} + +PosixError GetContentsFD(int fd, std::string* output) { + // Keep reading until we hit an EOF or an error. + while (true) { + char buf[16 * 1024] = {}; // Read in 16KB chunks. + int bytes_read = read(fd, buf, sizeof(buf)); + if (bytes_read < 0) { + if (errno == EINTR) { + continue; + } + return PosixError(errno, "GetContentsFD read failure."); + } + + if (bytes_read == 0) { + break; // EOF. + } + + output->append(buf, bytes_read); + } + return NoError(); +} + +PosixErrorOr ReadLink(absl::string_view path) { + char buf[PATH_MAX + 1] = {}; + int ret = readlink(std::string(path).c_str(), buf, PATH_MAX); + if (ret < 0) { + return PosixError(errno, absl::StrCat("readlink ", path)); + } + + return std::string(buf, ret); +} + +PosixError WalkTree( + absl::string_view path, bool recursive, + const std::function& cb) { + DIR* dir = opendir(std::string(path).c_str()); + if (dir == nullptr) { + return PosixError(errno, absl::StrCat("opendir ", path)); + } + auto dir_closer = Cleanup([&dir]() { closedir(dir); }); + while (true) { + // Readdir(3): If the end of the directory stream is reached, NULL is + // returned and errno is not changed. If an error occurs, NULL is returned + // and errno is set appropriately. To distinguish end of stream and from an + // error, set errno to zero before calling readdir() and then check the + // value of errno if NULL is returned. + errno = 0; + struct dirent* dp = readdir(dir); + if (dp == nullptr) { + if (errno != 0) { + return PosixError(errno, absl::StrCat("readdir ", path)); + } + break; // We're done. + } + + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { + // Skip dots. + continue; + } + + auto full_path = JoinPath(path, dp->d_name); + ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(full_path)); + if (S_ISDIR(s.st_mode) && recursive) { + RETURN_IF_ERRNO(WalkTree(full_path, recursive, cb)); + } else { + cb(full_path, s); + } + } + // We're done walking so let's invoke our cleanup callback now. + dir_closer.Release()(); + + // And we have to dispatch the callback on the base directory. + ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(path)); + cb(path, s); + + return NoError(); +} + +PosixErrorOr> ListDir(absl::string_view abspath, + bool skipdots) { + std::vector files; + + DIR* dir = opendir(std::string(abspath).c_str()); + if (dir == nullptr) { + return PosixError(errno, absl::StrCat("opendir ", abspath)); + } + auto dir_closer = Cleanup([&dir]() { closedir(dir); }); + while (true) { + // Readdir(3): If the end of the directory stream is reached, NULL is + // returned and errno is not changed. If an error occurs, NULL is returned + // and errno is set appropriately. To distinguish end of stream and from an + // error, set errno to zero before calling readdir() and then check the + // value of errno if NULL is returned. + errno = 0; + struct dirent* dp = readdir(dir); + if (dp == nullptr) { + if (errno != 0) { + return PosixError(errno, absl::StrCat("readdir ", abspath)); + } + break; // We're done. + } + + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { + if (skipdots) { + continue; + } + } + files.push_back(std::string(dp->d_name)); + } + + return files; +} + +PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs, + int* undeleted_files) { + ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); + if (!exists) { + return PosixError(ENOENT, absl::StrCat(path, " does not exist")); + } + + ASSIGN_OR_RETURN_ERRNO(bool dir, IsDirectory(path)); + if (!dir) { + // Nothing recursive needs to happen we can just call Delete. + auto status = Delete(path); + if (!status.ok() && undeleted_files) { + (*undeleted_files)++; + } + return status; + } + + return WalkTree(path, /*recursive=*/true, + [&](absl::string_view absolute_path, const struct stat& s) { + if (S_ISDIR(s.st_mode)) { + auto rm_status = Rmdir(absolute_path); + if (!rm_status.ok() && undeleted_dirs) { + (*undeleted_dirs)++; + } + } else { + auto delete_status = Delete(absolute_path); + if (!delete_status.ok() && undeleted_files) { + (*undeleted_files)++; + } + } + }); +} + +PosixError RecursivelyCreateDir(absl::string_view path) { + if (path.empty() || path == "/") { + return PosixError(EINVAL, "Cannot create root!"); + } + + // Does it already exist, if so we're done. + ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); + if (exists) { + return NoError(); + } + + // Do we need to create directories under us? + auto dirname = Dirname(path); + ASSIGN_OR_RETURN_ERRNO(exists, Exists(dirname)); + if (!exists) { + RETURN_IF_ERRNO(RecursivelyCreateDir(dirname)); + } + + return Mkdir(path); +} + +// Makes a path absolute with respect to an optional base. If no base is +// provided it will use the current working directory. +PosixErrorOr MakeAbsolute(absl::string_view filename, + absl::string_view base) { + if (filename.empty()) { + return PosixError(EINVAL, "filename cannot be empty."); + } + + if (filename[0] == '/') { + // This path is already absolute. + return std::string(filename); + } + + std::string actual_base; + if (!base.empty()) { + actual_base = std::string(base); + } else { + auto cwd_or = GetCWD(); + RETURN_IF_ERRNO(cwd_or.error()); + actual_base = cwd_or.ValueOrDie(); + } + + // Reverse iterate removing trailing slashes, effectively right trim '/'. + for (int i = actual_base.size() - 1; i >= 0 && actual_base[i] == '/'; --i) { + actual_base.erase(i, 1); + } + + if (filename == ".") { + return actual_base.empty() ? "/" : actual_base; + } + + return absl::StrCat(actual_base, "/", filename); +} + +std::string CleanPath(const absl::string_view unclean_path) { + std::string path = std::string(unclean_path); + const char *src = path.c_str(); + std::string::iterator dst = path.begin(); + + // Check for absolute path and determine initial backtrack limit. + const bool is_absolute_path = *src == '/'; + if (is_absolute_path) { + *dst++ = *src++; + while (*src == '/') ++src; + } + std::string::const_iterator backtrack_limit = dst; + + // Process all parts + while (*src) { + bool parsed = false; + + if (src[0] == '.') { + // 1dot ".", check for END or SEP. + if (src[1] == '/' || !src[1]) { + if (*++src) { + ++src; + } + parsed = true; + } else if (src[1] == '.' && (src[2] == '/' || !src[2])) { + // 2dot END or SEP (".." | "../"). + src += 2; + if (dst != backtrack_limit) { + // We can backtrack the previous part + for (--dst; dst != backtrack_limit && dst[-1] != '/'; --dst) { + // Empty. + } + } else if (!is_absolute_path) { + // Failed to backtrack and we can't skip it either. Rewind and copy. + src -= 2; + *dst++ = *src++; + *dst++ = *src++; + if (*src) { + *dst++ = *src; + } + // We can never backtrack over a copied "../" part so set new limit. + backtrack_limit = dst; + } + if (*src) { + ++src; + } + parsed = true; + } + } + + // If not parsed, copy entire part until the next SEP or EOS. + if (!parsed) { + while (*src && *src != '/') { + *dst++ = *src++; + } + if (*src) { + *dst++ = *src++; + } + } + + // Skip consecutive SEP occurrences + while (*src == '/') { + ++src; + } + } + + // Calculate and check the length of the cleaned path. + int path_length = dst - path.begin(); + if (path_length != 0) { + // Remove trailing '/' except if it is root path ("/" ==> path_length := 1) + if (path_length > 1 && path[path_length - 1] == '/') { + --path_length; + } + path.resize(path_length); + } else { + // The cleaned path is empty; assign "." as per the spec. + path.assign(1, '.'); + } + return path; +} + +PosixErrorOr GetRelativePath(absl::string_view source, + absl::string_view dest) { + if (!absl::StartsWith(source, "/") || !absl::StartsWith(dest, "/")) { + // At least one of the inputs is not an absolute path. + return PosixError( + EINVAL, + "GetRelativePath: At least one of the inputs is not an absolute path."); + } + const std::string clean_source = CleanPath(source); + const std::string clean_dest = CleanPath(dest); + auto source_parts = absl::StrSplit(clean_source, '/', absl::SkipEmpty()); + auto dest_parts = absl::StrSplit(clean_dest, '/', absl::SkipEmpty()); + auto source_iter = source_parts.begin(); + auto dest_iter = dest_parts.begin(); + + // Advance past common prefix. + while (source_iter != source_parts.end() && dest_iter != dest_parts.end() && + *source_iter == *dest_iter) { + ++source_iter; + ++dest_iter; + } + + // Build result backtracking. + std::string result = ""; + while (source_iter != source_parts.end()) { + absl::StrAppend(&result, "../"); + ++source_iter; + } + + // Add remaining path to dest. + while (dest_iter != dest_parts.end()) { + absl::StrAppend(&result, *dest_iter, "/"); + ++dest_iter; + } + + if (result.empty()) { + return std::string("."); + } + + // Remove trailing slash. + result.erase(result.size() - 1); + return result; +} + +absl::string_view Dirname(absl::string_view path) { + return SplitPath(path).first; +} + +absl::string_view Basename(absl::string_view path) { + return SplitPath(path).second; +} + +std::pair SplitPath( + absl::string_view path) { + std::string::size_type pos = path.find_last_of('/'); + + // Handle the case with no '/' in 'path'. + if (pos == absl::string_view::npos) + return std::make_pair(path.substr(0, 0), path); + + // Handle the case with a single leading '/' in 'path'. + if (pos == 0) + return std::make_pair(path.substr(0, 1), absl::ClippedSubstr(path, 1)); + + return std::make_pair(path.substr(0, pos), + absl::ClippedSubstr(path, pos + 1)); +} + +std::string JoinPath(absl::string_view path1, absl::string_view path2) { + if (path1.empty()) return std::string(path2); + if (path2.empty()) return std::string(path1); + if (path1.back() == '/') { + if (path2.front() == '/') + return absl::StrCat(path1, absl::ClippedSubstr(path2, 1)); + } else { + if (path2.front() != '/') return absl::StrCat(path1, "/", path2); + } + return absl::StrCat(path1, path2); +} + +PosixErrorOr ProcessExePath(int pid) { + if (pid <= 0) { + return PosixError(EINVAL, "Invalid pid specified"); + } + + return ReadLink(absl::StrCat("/proc/", pid, "/exe")); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/fs_util.h b/test/util/fs_util.h new file mode 100644 index 000000000..9412b2f71 --- /dev/null +++ b/test/util/fs_util.h @@ -0,0 +1,182 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_FS_UTIL_H_ +#define GVISOR_TEST_UTIL_FS_UTIL_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { +// Returns a status or the current working directory. +PosixErrorOr GetCWD(); + +// Returns true/false depending on whether or not path exists, or an error if it +// can't be determined. +PosixErrorOr Exists(absl::string_view path); + +// Returns a stat structure for the given path or an error. +PosixErrorOr Stat(absl::string_view path); + +// Deletes the file or directory at path or returns an error. +PosixError Delete(absl::string_view path); + +// Changes the mode of a file or returns an error. +PosixError Chmod(absl::string_view path, int mode); + +// Truncates a file to the given length or returns an error. +PosixError Truncate(absl::string_view path, int length); + +// Returns true/false depending on whether or not the path is a directory or +// returns an error. +PosixErrorOr IsDirectory(absl::string_view path); + +// Makes a directory or returns an error. +PosixError Mkdir(absl::string_view path, int mode = 0755); + +// Removes a directory or returns an error. +PosixError Rmdir(absl::string_view path); + +// Attempts to set the contents of a file or returns an error. +PosixError SetContents(absl::string_view path, absl::string_view contents); + +// Creates a file with the given contents and mode or returns an error. +PosixError CreateWithContents(absl::string_view path, + absl::string_view contents, int mode = 0666); + +// Attempts to read the entire contents of the file into the provided std::string +// buffer or returns an error. +PosixError GetContents(absl::string_view path, std::string* output); + +// Attempts to read the entire contents of the file or returns an error. +PosixErrorOr GetContents(absl::string_view path); + +// Attempts to read the entire contents of the provided fd into the provided +// std::string or returns an error. +PosixError GetContentsFD(int fd, std::string* output); + +// Attempts to read the entire contents of the provided fd or returns an error. +PosixErrorOr GetContentsFD(int fd); + +// Executes the readlink(2) system call or returns an error. +PosixErrorOr ReadLink(absl::string_view path); + +// WalkTree will walk a directory tree in a depth first search manner (if +// recursive). It will invoke a provided callback for each file and directory, +// the parent will always be invoked last making this appropriate for things +// such as deleting an entire directory tree. +// +// This method will return an error when it's unable to access the provided +// path, or when the path is not a directory. +PosixError WalkTree( + absl::string_view path, bool recursive, + const std::function& cb); + +// Returns the base filenames for all files under a given absolute path. If +// skipdots is true the returned vector will not contain "." or "..". This +// method does not walk the tree recursively it only returns the elements +// in that directory. +PosixErrorOr> ListDir(absl::string_view abspath, + bool skipdots); + +// Attempt to recursively delete a directory or file. Returns an error and +// the number of undeleted directories and files. If either +// undeleted_dirs or undeleted_files is nullptr then it will not be used. +PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs, + int* undeleted_files); + +// Recursively create the directory provided or return an error. +PosixError RecursivelyCreateDir(absl::string_view path); + +// Makes a path absolute with respect to an optional base. If no base is +// provided it will use the current working directory. +PosixErrorOr MakeAbsolute(absl::string_view filename, + absl::string_view base); + +// Generates a relative path from the source directory to the destination +// (dest) file or directory. This uses ../ when necessary for destinations +// which are not nested within the source. Both source and dest are required +// to be absolute paths, and an empty std::string will be returned if they are not. +PosixErrorOr GetRelativePath(absl::string_view source, + absl::string_view dest); + +// Returns the part of the path before the final "/", EXCEPT: +// * If there is a single leading "/" in the path, the result will be the +// leading "/". +// * If there is no "/" in the path, the result is the empty prefix of the +// input std::string. +absl::string_view Dirname(absl::string_view path); + +// Return the parts of the path, split on the final "/". If there is no +// "/" in the path, the first part of the output is empty and the second +// is the input. If the only "/" in the path is the first character, it is +// the first part of the output. +std::pair SplitPath( + absl::string_view path); + +// Returns the part of the path after the final "/". If there is no +// "/" in the path, the result is the same as the input. +// Note that this function's behavior differs from the Unix basename +// command if path ends with "/". For such paths, this function returns the +// empty std::string. +absl::string_view Basename(absl::string_view path); + +// Collapse duplicate "/"s, resolve ".." and "." path elements, remove +// trailing "/". +// +// NOTE: This respects relative vs. absolute paths, but does not +// invoke any system calls (getcwd(2)) in order to resolve relative +// paths wrt actual working directory. That is, this is purely a +// std::string manipulation, completely independent of process state. +std::string CleanPath(absl::string_view path); + +// Returns the full path to the executable of the given pid or a PosixError. +PosixErrorOr ProcessExePath(int pid); + +namespace internal { +// Not part of the public API. +std::string JoinPathImpl(std::initializer_list paths); +} // namespace internal + +// Join multiple paths together. +// All paths will be treated as relative paths, regardless of whether or not +// they start with a leading '/'. That is, all paths will be concatenated +// together, with the appropriate path separator inserted in between. +// Arguments must be convertible to absl::string_view. +// +// Usage: +// std::string path = JoinPath("/foo", dirname, filename); +// std::string path = JoinPath(FLAGS_test_srcdir, filename); +// +// 0, 1, 2-path specializations exist to optimize common cases. +inline std::string JoinPath() { return std::string(); } +inline std::string JoinPath(absl::string_view path) { + return std::string(path.data(), path.size()); +} + +std::string JoinPath(absl::string_view path1, absl::string_view path2); +template +inline std::string JoinPath(absl::string_view path1, absl::string_view path2, + absl::string_view path3, const T&... args) { + return internal::JoinPathImpl({path1, path2, path3, args...}); +} +} // namespace testing +} // namespace gvisor +#endif // GVISOR_TEST_UTIL_FS_UTIL_H_ diff --git a/test/util/fs_util_test.cc b/test/util/fs_util_test.cc new file mode 100644 index 000000000..ce70d58aa --- /dev/null +++ b/test/util/fs_util_test.cc @@ -0,0 +1,100 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(FsUtilTest, RecursivelyCreateDirManualDelete) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string base_path = JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); + + ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); + ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); + + // Delete everything until we hit root and then stop, we want to try this + // without using RecursivelyDelete. + std::string cur_path = base_path; + while (cur_path != root.path()) { + ASSERT_THAT(Exists(cur_path), IsPosixErrorOkAndHolds(true)); + ASSERT_NO_ERRNO(Rmdir(cur_path)); + ASSERT_THAT(Exists(cur_path), IsPosixErrorOkAndHolds(false)); + auto dir = Dirname(cur_path); + cur_path = std::string(dir); + } +} + +TEST(FsUtilTest, RecursivelyCreateAndDeleteDir) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string base_path = JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); + + ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); + ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); + + const std::string sub_path = JoinPath(root.path(), "a"); + ASSERT_NO_ERRNO(RecursivelyDelete(sub_path, nullptr, nullptr)); + ASSERT_THAT(Exists(sub_path), IsPosixErrorOkAndHolds(false)); +} + +TEST(FsUtilTest, RecursivelyCreateAndDeletePartial) { + const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + const std::string base_path = JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); + + ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); + ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); + + const std::string a = JoinPath(root.path(), "a"); + auto listing = ASSERT_NO_ERRNO_AND_VALUE(ListDir(a, true)); + ASSERT_THAT(listing, ::testing::Contains("b")); + ASSERT_EQ(listing.size(), 1); + + listing = ASSERT_NO_ERRNO_AND_VALUE(ListDir(a, false)); + ASSERT_THAT(listing, ::testing::Contains(".")); + ASSERT_THAT(listing, ::testing::Contains("..")); + ASSERT_THAT(listing, ::testing::Contains("b")); + ASSERT_EQ(listing.size(), 3); + + const std::string sub_path = JoinPath(root.path(), "/a/b/c/d/e/f"); + + ASSERT_NO_ERRNO( + CreateWithContents(JoinPath(Dirname(sub_path), "file"), "Hello World")); + std::string contents = ""; + ASSERT_NO_ERRNO(GetContents(JoinPath(Dirname(sub_path), "file"), &contents)); + ASSERT_EQ(contents, "Hello World"); + + ASSERT_NO_ERRNO(RecursivelyDelete(sub_path, nullptr, nullptr)); + ASSERT_THAT(Exists(sub_path), IsPosixErrorOkAndHolds(false)); + + // The parent of the subpath (directory e) should still exist. + ASSERT_THAT(Exists(Dirname(sub_path)), IsPosixErrorOkAndHolds(true)); + + // The file we created along side f should also still exist. + ASSERT_THAT(Exists(JoinPath(Dirname(sub_path), "file")), + IsPosixErrorOkAndHolds(true)); +} +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/util/logging.cc b/test/util/logging.cc new file mode 100644 index 000000000..86ea71df3 --- /dev/null +++ b/test/util/logging.cc @@ -0,0 +1,97 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/logging.h" + +#include +#include +#include +#include + +namespace gvisor { +namespace testing { + +namespace { + +// We implement this here instead of using test_util to avoid cyclic +// dependencies. +int Write(int fd, const char* buf, size_t size) { + size_t written = 0; + while (written < size) { + int res = write(fd, buf + written, size - written); + if (res < 0 && errno == EINTR) { + continue; + } else if (res <= 0) { + break; + } + + written += res; + } + return static_cast(written); +} + +// Write 32-bit decimal number to fd. +int WriteNumber(int fd, uint32_t val) { + constexpr char kDigits[] = "0123456789"; + constexpr int kBase = 10; + + // 10 chars for 32-bit number in decimal, 1 char for the NUL-terminator. + constexpr int kBufferSize = 11; + char buf[kBufferSize]; + + // Convert the number to std::string. + char* s = buf + sizeof(buf) - 1; + size_t size = 0; + + *s = '\0'; + do { + s--; + size++; + + *s = kDigits[val % kBase]; + val /= kBase; + } while (val); + + return Write(fd, s, size); +} + +} // namespace + +void CheckFailure(const char* cond, size_t cond_size, const char* msg, + size_t msg_size, bool include_errno) { + int saved_errno = errno; + + constexpr char kCheckFailure[] = "Check failed: "; + Write(2, kCheckFailure, sizeof(kCheckFailure) - 1); + Write(2, cond, cond_size); + + if (msg != nullptr) { + Write(2, ": ", 2); + Write(2, msg, msg_size); + } + + if (include_errno) { + constexpr char kErrnoMessage[] = " (errno "; + Write(2, kErrnoMessage, sizeof(kErrnoMessage) - 1); + WriteNumber(2, saved_errno); + Write(2, ")", 1); + } + + Write(2, "\n", 1); + + abort(); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/logging.h b/test/util/logging.h new file mode 100644 index 000000000..6e957b172 --- /dev/null +++ b/test/util/logging.h @@ -0,0 +1,73 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_LOGGING_H_ +#define GVISOR_TEST_UTIL_LOGGING_H_ + +#include + +namespace gvisor { +namespace testing { + +void CheckFailure(const char* cond, size_t cond_size, const char* msg, + size_t msg_size, bool include_errno); + +// If cond is false, aborts the current process. +// +// This macro is async-signal-safe. +#define TEST_CHECK(cond) \ + do { \ + if (!(cond)) { \ + ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, nullptr, \ + 0, false); \ + } \ + } while (0) + +// If cond is false, logs msg then aborts the current process. +// +// This macro is async-signal-safe. +#define TEST_CHECK_MSG(cond, msg) \ + do { \ + if (!(cond)) { \ + ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, msg, \ + sizeof(msg) - 1, false); \ + } \ + } while (0) + +// If cond is false, logs errno, then aborts the current process. +// +// This macro is async-signal-safe. +#define TEST_PCHECK(cond) \ + do { \ + if (!(cond)) { \ + ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, nullptr, \ + 0, true); \ + } \ + } while (0) + +// If cond is false, logs msg and errno, then aborts the current process. +// +// This macro is async-signal-safe. +#define TEST_PCHECK_MSG(cond, msg) \ + do { \ + if (!(cond)) { \ + ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, msg, \ + sizeof(msg) - 1, true); \ + } \ + } while (0) + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_LOGGING_H_ diff --git a/test/util/memory_util.h b/test/util/memory_util.h new file mode 100644 index 000000000..8f6e99ba6 --- /dev/null +++ b/test/util/memory_util.h @@ -0,0 +1,124 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_MEMORY_UTIL_H_ +#define GVISOR_TEST_UTIL_MEMORY_UTIL_H_ + +#include +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "test/util/logging.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// RAII type for mmap'ed memory. Only usable in tests due to use of a test-only +// macro that can't be named without invoking the presubmit's wrath. +class Mapping { + public: + // Constructs a mapping that owns nothing. + Mapping() = default; + + // Constructs a mapping that owns the mmapped memory [ptr, ptr+len). Most + // users should use Mmap or MmapAnon instead. + Mapping(void* ptr, size_t len) : ptr_(ptr), len_(len) {} + + Mapping(Mapping&& orig) : ptr_(orig.ptr_), len_(orig.len_) { orig.release(); } + + Mapping& operator=(Mapping&& orig) { + ptr_ = orig.ptr_; + len_ = orig.len_; + orig.release(); + return *this; + } + + Mapping(Mapping const&) = delete; + Mapping& operator=(Mapping const&) = delete; + + ~Mapping() { reset(); } + + void* ptr() const { return ptr_; } + size_t len() const { return len_; } + + // Returns a pointer to the end of the mapping. Useful for when the mapping + // is used as a thread stack. + void* endptr() const { return reinterpret_cast(addr() + len_); } + + // Returns the start of this mapping cast to uintptr_t for ease of pointer + // arithmetic. + uintptr_t addr() const { return reinterpret_cast(ptr_); } + + // Returns the end of this mapping cast to uintptr_t for ease of pointer + // arithmetic. + uintptr_t endaddr() const { return reinterpret_cast(endptr()); } + + // Returns this mapping as a StringPiece for ease of comparison. + // + // This function is named view in anticipation of the eventual replacement of + // StringPiece with std::string_view. + absl::string_view view() const { + return absl::string_view(static_cast(ptr_), len_); + } + + // These are both named reset for consistency with standard smart pointers. + + void reset(void* ptr, size_t len) { + if (len_) { + TEST_PCHECK(munmap(ptr_, len_) == 0); + } + ptr_ = ptr; + len_ = len; + } + + void reset() { reset(nullptr, 0); } + + void release() { + ptr_ = nullptr; + len_ = 0; + } + + private: + void* ptr_ = nullptr; + size_t len_ = 0; +}; + +// Wrapper around mmap(2) that returns a Mapping. +inline PosixErrorOr Mmap(void* addr, size_t length, int prot, + int flags, int fd, off_t offset) { + void* ptr = mmap(addr, length, prot, flags, fd, offset); + if (ptr == MAP_FAILED) { + return PosixError( + errno, absl::StrFormat("mmap(%p, %d, %x, %x, %d, %d)", addr, length, + prot, flags, fd, offset)); + } + MaybeSave(); + return Mapping(ptr, length); +} + +// Convenience wrapper around Mmap for anonymous mappings. +inline PosixErrorOr MmapAnon(size_t length, int prot, int flags) { + return Mmap(nullptr, length, prot, flags | MAP_ANONYMOUS, -1, 0); +} + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_MEMORY_UTIL_H_ diff --git a/test/util/mount_util.h b/test/util/mount_util.h new file mode 100644 index 000000000..468170646 --- /dev/null +++ b/test/util/mount_util.h @@ -0,0 +1,48 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_MOUNT_UTIL_H_ +#define GVISOR_TEST_UTIL_MOUNT_UTIL_H_ + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/util/cleanup.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// Mount mounts the filesystem, and unmounts when the returned reference is +// destroyed. +inline PosixErrorOr Mount(const std::string &source, const std::string &target, + const std::string &fstype, uint64_t mountflags, + const std::string &data, uint64_t umountflags) { + if (mount(source.c_str(), target.c_str(), fstype.c_str(), mountflags, + data.c_str()) == -1) { + return PosixError(errno, "mount failed"); + } + return Cleanup([target, umountflags]() { + EXPECT_THAT(umount2(target.c_str(), umountflags), SyscallSucceeds()); + }); +} + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_MOUNT_UTIL_H_ diff --git a/test/util/multiprocess_util.cc b/test/util/multiprocess_util.cc new file mode 100644 index 000000000..12637db8c --- /dev/null +++ b/test/util/multiprocess_util.cc @@ -0,0 +1,139 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/multiprocess_util.h" + +#include +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +PosixErrorOr ForkAndExec(const std::string& filename, + const ExecveArray& argv, + const ExecveArray& envv, + const std::function& fn, pid_t* child, + int* execve_errno) { + int pfds[2]; + int ret = pipe2(pfds, O_CLOEXEC); + if (ret < 0) { + return PosixError(errno, "pipe failed"); + } + FileDescriptor rfd(pfds[0]); + FileDescriptor wfd(pfds[1]); + + int parent_stdout = dup(STDOUT_FILENO); + if (parent_stdout < 0) { + return PosixError(errno, "dup stdout"); + } + int parent_stderr = dup(STDERR_FILENO); + if (parent_stdout < 0) { + return PosixError(errno, "dup stderr"); + } + + pid_t pid = fork(); + if (pid < 0) { + return PosixError(errno, "fork failed"); + } else if (pid == 0) { + // Child. + rfd.reset(); + if (dup2(parent_stdout, STDOUT_FILENO) < 0) { + _exit(3); + } + if (dup2(parent_stderr, STDERR_FILENO) < 0) { + _exit(4); + } + close(parent_stdout); + close(parent_stderr); + + // Clean ourself up in case the parent doesn't. + if (prctl(PR_SET_PDEATHSIG, SIGKILL)) { + _exit(3); + } + + if (fn) { + fn(); + } + + execve(filename.c_str(), argv.get(), envv.get()); + int error = errno; + if (WriteFd(pfds[1], &error, sizeof(error)) != sizeof(error)) { + // We can't do much if the write fails, but we can at least exit with a + // different code. + _exit(2); + } + _exit(1); + } + + // Parent. + if (child) { + *child = pid; + } + + auto cleanup = Cleanup([pid] { + kill(pid, SIGKILL); + RetryEINTR(waitpid)(pid, nullptr, 0); + }); + + wfd.reset(); + + int read_errno; + ret = ReadFd(rfd.get(), &read_errno, sizeof(read_errno)); + if (ret == 0) { + // Other end of the pipe closed, execve must have succeeded. + read_errno = 0; + } else if (ret < 0) { + return PosixError(errno, "read pipe failed"); + } else if (ret != sizeof(read_errno)) { + return PosixError(EPIPE, absl::StrCat("pipe read wrong size ", ret)); + } + + if (execve_errno) { + *execve_errno = read_errno; + } + + return std::move(cleanup); +} + +PosixErrorOr InForkedProcess(const std::function& fn) { + pid_t pid = fork(); + if (pid == 0) { + fn(); + _exit(0); + } + MaybeSave(); + if (pid < 0) { + return PosixError(errno, "fork failed"); + } + + int status; + if (waitpid(pid, &status, 0) < 0) { + return PosixError(errno, "waitpid failed"); + } + + return status; +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/multiprocess_util.h b/test/util/multiprocess_util.h new file mode 100644 index 000000000..c09d6167f --- /dev/null +++ b/test/util/multiprocess_util.h @@ -0,0 +1,113 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ +#define GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ + +#include + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "test/util/cleanup.h" +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { + +// Immutable holder for a dynamically-sized array of pointers to mutable char, +// terminated by a null pointer, as required for the argv and envp arguments to +// execve(2). +class ExecveArray { + public: + // Constructs an empty ExecveArray. + ExecveArray() = default; + + // Constructs an ExecveArray by copying strings from the given range. T must + // be a range over ranges of char. + template + explicit ExecveArray(T const& strs) : ExecveArray(strs.begin(), strs.end()) {} + + // Constructs an ExecveArray by copying strings from [first, last). InputIt + // must be an input iterator over a range over char. + template + ExecveArray(InputIt first, InputIt last) { + std::vector offsets; + auto output_it = std::back_inserter(str_); + for (InputIt it = first; it != last; ++it) { + offsets.push_back(str_.size()); + auto const& s = *it; + std::copy(s.begin(), s.end(), output_it); + str_.push_back('\0'); + } + ptrs_.reserve(offsets.size() + 1); + for (auto offset : offsets) { + ptrs_.push_back(str_.data() + offset); + } + ptrs_.push_back(nullptr); + } + + // Constructs an ExecveArray by copying strings from list. This overload must + // exist independently of the single-argument template constructor because + // std::initializer_list does not participate in template argument deduction + // (i.e. cannot be type-inferred in an invocation of the templated + // constructor). + /* implicit */ ExecveArray(std::initializer_list list) + : ExecveArray(list.begin(), list.end()) {} + + // Disable move construction and assignment since ptrs_ points into str_. + ExecveArray(ExecveArray&&) = delete; + ExecveArray& operator=(ExecveArray&&) = delete; + + char* const* get() const { return ptrs_.data(); } + + private: + std::vector str_; + std::vector ptrs_; +}; + +// Simplified version of SubProcess. Returns OK and a cleanup function to kill +// the child if it made it to execve. +// +// fn is run between fork and exec. If it needs to fail, it should exit the +// process. +// +// The child pid is returned via child, if provided. +// execve's error code is returned via execve_errno, if provided. +PosixErrorOr ForkAndExec(const std::string& filename, + const ExecveArray& argv, + const ExecveArray& envv, + const std::function& fn, pid_t* child, + int* execve_errno); + +inline PosixErrorOr ForkAndExec(const std::string& filename, + const ExecveArray& argv, + const ExecveArray& envv, pid_t* child, + int* execve_errno) { + return ForkAndExec(filename, argv, envv, [] {}, child, execve_errno); +} + +// Calls fn in a forked subprocess and returns the exit status of the +// subprocess. +// +// fn must be async-signal-safe. +PosixErrorOr InForkedProcess(const std::function& fn); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ diff --git a/test/util/posix_error.cc b/test/util/posix_error.cc new file mode 100644 index 000000000..9db72c6de --- /dev/null +++ b/test/util/posix_error.cc @@ -0,0 +1,93 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/posix_error.h" + +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" + +namespace gvisor { +namespace testing { + +std::string PosixError::ToString() const { + if (ok()) { + return "No Error"; + } + + std::string ret; + + char strerrno_buf[1024] = {}; + char* msg = nullptr; + if ((msg = strerror_r(errno_, strerrno_buf, sizeof(strerrno_buf))) == + nullptr) { + ret = absl::StrCat("PosixError(errno=", errno_, " strerror_r FAILED)"); + } else { + ret = absl::StrCat("PosixError(errno=", errno_, " ", msg, ")"); + } + + if (!msg_.empty()) { + ret.append(" "); + ret.append(msg_); + } + + return ret; +} + +::std::ostream& operator<<(::std::ostream& os, const PosixError& e) { + os << e.ToString(); + return os; +} + +void PosixErrorIsMatcherCommonImpl::DescribeTo(std::ostream* os) const { + *os << "has an errno value that "; + code_matcher_.DescribeTo(os); + *os << ", and has an error message that "; + message_matcher_.DescribeTo(os); +} + +void PosixErrorIsMatcherCommonImpl::DescribeNegationTo(std::ostream* os) const { + *os << "has an errno value that "; + code_matcher_.DescribeNegationTo(os); + *os << ", or has an error message that "; + message_matcher_.DescribeNegationTo(os); +} + +bool PosixErrorIsMatcherCommonImpl::MatchAndExplain( + const PosixError& error, + ::testing::MatchResultListener* result_listener) const { + ::testing::StringMatchResultListener inner_listener; + + inner_listener.Clear(); + if (!code_matcher_.MatchAndExplain(error.errno_value(), &inner_listener)) { + *result_listener << (inner_listener.str().empty() + ? "whose errno value is wrong" + : "which has a errno value " + + inner_listener.str()); + return false; + } + + if (!message_matcher_.Matches(error.error_message())) { + *result_listener << "whose error message is wrong"; + return false; + } + + return true; +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/posix_error.h b/test/util/posix_error.h new file mode 100644 index 000000000..8450be9b9 --- /dev/null +++ b/test/util/posix_error.h @@ -0,0 +1,428 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_POSIX_ERROR_H_ +#define GVISOR_TEST_UTIL_POSIX_ERROR_H_ + +#include + +#include "gmock/gmock.h" +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "absl/types/variant.h" +#include "test/util/logging.h" + +namespace gvisor { +namespace testing { + +class PosixErrorIsMatcherCommonImpl; + +template +class PosixErrorOr; + +class ABSL_MUST_USE_RESULT PosixError { + public: + PosixError() {} + explicit PosixError(int errno_value) : errno_(errno_value) {} + PosixError(int errno_value, std::string msg) + : errno_(errno_value), msg_(std::move(msg)) {} + + PosixError(PosixError&& other) = default; + PosixError& operator=(PosixError&& other) = default; + PosixError(const PosixError&) = default; + PosixError& operator=(const PosixError&) = default; + + bool ok() const { return errno_ == 0; } + + // Returns a reference to *this to make matchers compatible with + // PosixErrorOr. + const PosixError& error() const { return *this; } + + std::string error_message() const { return msg_; } + + // ToString produces a full std::string representation of this posix error + // including the printable representation of the errno and the error message. + std::string ToString() const; + + // Ignores any errors. This method does nothing except potentially suppress + // complaints from any tools that are checking that errors are not dropped on + // the floor. + void IgnoreError() const {} + + private: + int errno_value() const { return errno_; } + int errno_ = 0; + std::string msg_; + + friend class PosixErrorIsMatcherCommonImpl; + + template + friend class PosixErrorOr; +}; + +template +class ABSL_MUST_USE_RESULT PosixErrorOr { + public: + PosixErrorOr(const PosixError& error); // NOLINT + explicit PosixErrorOr(const T& value); + PosixErrorOr(T&& value); // NOLINT + + PosixErrorOr(PosixErrorOr&& other) = default; + PosixErrorOr& operator=(PosixErrorOr&& other) = default; + PosixErrorOr(const PosixErrorOr&) = default; + PosixErrorOr& operator=(const PosixErrorOr&) = default; + + // Conversion copy/move constructor, T must be convertible from U. + template + friend class PosixErrorOr; + + template + PosixErrorOr(PosixErrorOr other); + + template + PosixErrorOr& operator=(PosixErrorOr other); + + // Return a reference to the error or NoError(). + const PosixError error() const; + + // Returns this->error().error_message(); + const std::string error_message() const; + + // Returns this->error().ok() + bool ok() const; + + // Returns a reference to our current value, or CHECK-fails if !this->ok(). + const T& ValueOrDie() const; + T& ValueOrDie(); + + // Ignores any errors. This method does nothing except potentially suppress + // complaints from any tools that are checking that errors are not dropped on + // the floor. + void IgnoreError() const {} + + private: + const int errno_value() const; + absl::variant value_; + + friend class PosixErrorIsMatcherCommonImpl; +}; + +template +PosixErrorOr::PosixErrorOr(const PosixError& error) : value_(error) {} + +template +PosixErrorOr::PosixErrorOr(const T& value) : value_(value) {} + +template +PosixErrorOr::PosixErrorOr(T&& value) : value_(std::move(value)) {} + +// Conversion copy/move constructor, T must be convertible from U. +template +template +inline PosixErrorOr::PosixErrorOr(PosixErrorOr other) { + if (absl::holds_alternative(other.value_)) { + // T is convertible from U. + value_ = absl::get(std::move(other.value_)); + } else if (absl::holds_alternative(other.value_)) { + value_ = absl::get(std::move(other.value_)); + } else { + TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); + } +} + +template +template +inline PosixErrorOr& PosixErrorOr::operator=(PosixErrorOr other) { + if (absl::holds_alternative(other.value_)) { + // T is convertible from U. + value_ = absl::get(std::move(other.value_)); + } else if (absl::holds_alternative(other.value_)) { + value_ = absl::get(std::move(other.value_)); + } else { + TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); + } + return *this; +} + +template +const PosixError PosixErrorOr::error() const { + if (!absl::holds_alternative(value_)) { + return PosixError(); + } + return absl::get(value_); +} + +template +const int PosixErrorOr::errno_value() const { + return error().errno_value(); +} + +template +const std::string PosixErrorOr::error_message() const { + return error().error_message(); +} + +template +bool PosixErrorOr::ok() const { + return error().ok(); +} + +template +const T& PosixErrorOr::ValueOrDie() const { + TEST_CHECK(absl::holds_alternative(value_)); + return absl::get(value_); +} + +template +T& PosixErrorOr::ValueOrDie() { + TEST_CHECK(absl::holds_alternative(value_)); + return absl::get(value_); +} + +extern ::std::ostream& operator<<(::std::ostream& os, const PosixError& e); + +template +::std::ostream& operator<<(::std::ostream& os, const PosixErrorOr& e) { + os << e.error(); + return os; +} + +// NoError is a PosixError that represents a successful state, i.e. No Error. +inline PosixError NoError() { return PosixError(); } + +// Monomorphic implementation of matcher IsPosixErrorOk() for a given type T. +// T can be PosixError, PosixErrorOr<>, or a reference to either of them. +template +class MonoPosixErrorIsOkMatcherImpl : public ::testing::MatcherInterface { + public: + void DescribeTo(std::ostream* os) const override { *os << "is OK"; } + void DescribeNegationTo(std::ostream* os) const override { + *os << "is not OK"; + } + bool MatchAndExplain(T actual_value, + ::testing::MatchResultListener*) const override { + return actual_value.ok(); + } +}; + +// Implements IsPosixErrorOkMatcher() as a polymorphic matcher. +class IsPosixErrorOkMatcher { + public: + template + operator ::testing::Matcher() const { // NOLINT + return MakeMatcher(new MonoPosixErrorIsOkMatcherImpl()); + } +}; + +// Monomorphic implementation of a matcher for a PosixErrorOr. +template +class IsPosixErrorOkAndHoldsMatcherImpl + : public ::testing::MatcherInterface { + public: + using ValueType = typename std::remove_reference().ValueOrDie())>::type; + + template + explicit IsPosixErrorOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) + : inner_matcher_(::testing::SafeMatcherCast( + std::forward(inner_matcher))) {} + + void DescribeTo(std::ostream* os) const override { + *os << "is OK and has a value that "; + inner_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const override { + *os << "isn't OK or has a value that "; + inner_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain( + PosixErrorOrType actual_value, + ::testing::MatchResultListener* listener) const override { + if (!actual_value.ok()) { + *listener << "which has error value " << actual_value.error(); + return false; + } + + ::testing::StringMatchResultListener inner_listener; + const bool matches = inner_matcher_.MatchAndExplain( + actual_value.ValueOrDie(), &inner_listener); + const std::string inner_explanation = inner_listener.str(); + if (!inner_explanation.empty()) { + *listener << "which contains value " + << ::testing::PrintToString(actual_value.ValueOrDie()) << ", " + << inner_explanation; + } + return matches; + } + + private: + const ::testing::Matcher inner_matcher_; +}; + +// Implements IsOkAndHolds() as a polymorphic matcher. +template +class IsPosixErrorOkAndHoldsMatcher { + public: + explicit IsPosixErrorOkAndHoldsMatcher(InnerMatcher inner_matcher) + : inner_matcher_(std::move(inner_matcher)) {} + + // Converts this polymorphic matcher to a monomorphic one of the given type. + // PosixErrorOrType can be either PosixErrorOr or a reference to + // PosixErrorOr. + template + operator ::testing::Matcher() const { // NOLINT + return ::testing::MakeMatcher( + new IsPosixErrorOkAndHoldsMatcherImpl( + inner_matcher_)); + } + + private: + const InnerMatcher inner_matcher_; +}; + +// PosixErrorIs() is a polymorphic matcher. This class is the common +// implementation of it shared by all types T where PosixErrorIs() can be +// used as a Matcher. +class PosixErrorIsMatcherCommonImpl { + public: + PosixErrorIsMatcherCommonImpl( + ::testing::Matcher code_matcher, + ::testing::Matcher message_matcher) + : code_matcher_(std::move(code_matcher)), + message_matcher_(std::move(message_matcher)) {} + + void DescribeTo(std::ostream* os) const; + + void DescribeNegationTo(std::ostream* os) const; + + bool MatchAndExplain(const PosixError& error, + ::testing::MatchResultListener* result_listener) const; + + private: + const ::testing::Matcher code_matcher_; + const ::testing::Matcher message_matcher_; +}; + +// Monomorphic implementation of matcher PosixErrorIs() for a given type +// T. T can be PosixError, PosixErrorOr<>, or a reference to either of them. +template +class MonoPosixErrorIsMatcherImpl : public ::testing::MatcherInterface { + public: + explicit MonoPosixErrorIsMatcherImpl( + PosixErrorIsMatcherCommonImpl common_impl) + : common_impl_(std::move(common_impl)) {} + + void DescribeTo(std::ostream* os) const override { + common_impl_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const override { + common_impl_.DescribeNegationTo(os); + } + + bool MatchAndExplain( + T actual_value, + ::testing::MatchResultListener* result_listener) const override { + return common_impl_.MatchAndExplain(actual_value.error(), result_listener); + } + + private: + PosixErrorIsMatcherCommonImpl common_impl_; +}; + +inline ::testing::Matcher ToErrorCodeMatcher( + const ::testing::Matcher& m) { + return m; +} + +// Implements PosixErrorIs() as a polymorphic matcher. +class PosixErrorIsMatcher { + public: + template + PosixErrorIsMatcher(ErrorCodeMatcher&& code_matcher, + ::testing::Matcher message_matcher) + : common_impl_( + ToErrorCodeMatcher(std::forward(code_matcher)), + std::move(message_matcher)) {} + + // Converts this polymorphic matcher to a monomorphic matcher of the + // given type. T can be StatusOr<>, Status, or a reference to + // either of them. + template + operator ::testing::Matcher() const { // NOLINT + return MakeMatcher(new MonoPosixErrorIsMatcherImpl(common_impl_)); + } + + private: + const PosixErrorIsMatcherCommonImpl common_impl_; +}; + +// Returns a gMock matcher that matches a PosixError or PosixErrorOr<> whose +// whose error code matches code_matcher, and whose error message matches +// message_matcher. +template +PosixErrorIsMatcher PosixErrorIs( + ErrorCodeMatcher&& code_matcher, + ::testing::Matcher message_matcher) { + return PosixErrorIsMatcher(std::forward(code_matcher), + std::move(message_matcher)); +} + +// Returns a gMock matcher that matches a PosixErrorOr<> which is ok() and +// value matches the inner matcher. +template +IsPosixErrorOkAndHoldsMatcher::type> +IsPosixErrorOkAndHolds(InnerMatcher&& inner_matcher) { + return IsPosixErrorOkAndHoldsMatcher::type>( + std::forward(inner_matcher)); +} + +// Internal helper for concatenating macro values. +#define POSIX_ERROR_IMPL_CONCAT_INNER_(x, y) x##y +#define POSIX_ERROR_IMPL_CONCAT_(x, y) POSIX_ERROR_IMPL_CONCAT_INNER_(x, y) + +#define POSIX_ERROR_IMPL_ASSIGN_OR_RETURN_(posixerroror, lhs, rexpr) \ + auto posixerroror = (rexpr); \ + if (!posixerroror.ok()) { \ + return (posixerroror.error()); \ + } \ + lhs = std::move(posixerroror.ValueOrDie()) + +#define EXPECT_NO_ERRNO(expression) \ + EXPECT_THAT(expression, IsPosixErrorOkMatcher()) +#define ASSERT_NO_ERRNO(expression) \ + ASSERT_THAT(expression, IsPosixErrorOkMatcher()) + +#define ASSIGN_OR_RETURN_ERRNO(lhs, rexpr) \ + POSIX_ERROR_IMPL_ASSIGN_OR_RETURN_( \ + POSIX_ERROR_IMPL_CONCAT_(_status_or_value, __LINE__), lhs, rexpr) + +#define RETURN_IF_ERRNO(s) \ + do { \ + if (!s.ok()) return s; \ + } while (false); + +#define ASSERT_NO_ERRNO_AND_VALUE(expr) \ + ({ \ + auto _expr_result = (expr); \ + ASSERT_NO_ERRNO(_expr_result); \ + std::move(_expr_result.ValueOrDie()); \ + }) + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_POSIX_ERROR_H_ diff --git a/test/util/posix_error_test.cc b/test/util/posix_error_test.cc new file mode 100644 index 000000000..535b9f66a --- /dev/null +++ b/test/util/posix_error_test.cc @@ -0,0 +1,45 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/posix_error.h" + +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace gvisor { +namespace testing { + +namespace { + +TEST(PosixErrorTest, PosixError) { + auto err = PosixError(EAGAIN); + EXPECT_THAT(err, PosixErrorIs(EAGAIN, "")); +} + +TEST(PosixErrorTest, PosixErrorOrPosixError) { + auto err = PosixErrorOr(PosixError(EAGAIN)); + EXPECT_THAT(err, PosixErrorIs(EAGAIN, "")); +} + +TEST(PosixErrorTest, PosixErrorOrNullptr) { + auto err = PosixErrorOr(nullptr); + EXPECT_THAT(err, PosixErrorIs(0, "")); + EXPECT_NO_ERRNO(err); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/util/proc_util.cc b/test/util/proc_util.cc new file mode 100644 index 000000000..72f7e67d0 --- /dev/null +++ b/test/util/proc_util.cc @@ -0,0 +1,98 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/proc_util.h" + +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "test/util/fs_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// Parses a single line from /proc//maps. +PosixErrorOr ParseProcMapsLine(absl::string_view line) { + ProcMapsEntry map_entry = {}; + std::vector parts = absl::StrSplit(line, ' ', absl::SkipEmpty()); + + // A size of 5 means there is no file name specified. + if (parts.size() != 5 && parts.size() != 6) { + return PosixError(EINVAL, absl::StrCat("Invalid line: ", line)); + } + + // Address range in the form X-X where X are hex values without leading 0x. + std::vector addresses = absl::StrSplit(parts[0], '-'); + if (addresses.size() != 2) { + return PosixError(EINVAL, + absl::StrCat("Invalid address range: ", parts[0])); + } + ASSIGN_OR_RETURN_ERRNO(map_entry.start, AtoiBase(addresses[0], 16)); + ASSIGN_OR_RETURN_ERRNO(map_entry.end, AtoiBase(addresses[1], 16)); + + // Permissions are four bytes of the form rwxp or - if permission not set. + if (parts[1].size() != 4) { + return PosixError(EINVAL, + absl::StrCat("Invalid permission field: ", parts[1])); + } + + map_entry.readable = parts[1][0] == 'r'; + map_entry.writable = parts[1][1] == 'w'; + map_entry.executable = parts[1][2] == 'x'; + map_entry.priv = parts[1][3] == 'p'; + + ASSIGN_OR_RETURN_ERRNO(map_entry.offset, AtoiBase(parts[2], 16)); + + std::vector device = absl::StrSplit(parts[3], ':'); + if (device.size() != 2) { + return PosixError(EINVAL, absl::StrCat("Invalid device: ", parts[3])); + } + ASSIGN_OR_RETURN_ERRNO(map_entry.major, AtoiBase(device[0], 16)); + ASSIGN_OR_RETURN_ERRNO(map_entry.minor, AtoiBase(device[1], 16)); + + ASSIGN_OR_RETURN_ERRNO(map_entry.inode, Atoi(parts[4])); + if (parts.size() == 6) { + // A filename is present. + map_entry.filename = parts[5]; + } + + return map_entry; +} + +PosixErrorOr> ParseProcMaps( + absl::string_view contents) { + std::vector entries; + auto lines = absl::StrSplit(contents, '\n', absl::SkipEmpty()); + for (const auto& l : lines) { + LOG(INFO) << "line: " << l; + ASSIGN_OR_RETURN_ERRNO(auto entry, ParseProcMapsLine(l)); + entries.push_back(entry); + } + return entries; +} + +PosixErrorOr IsVsyscallEnabled() { + ASSIGN_OR_RETURN_ERRNO(auto contents, GetContents("/proc/self/maps")); + ASSIGN_OR_RETURN_ERRNO(auto maps, ParseProcMaps(contents)); + return std::any_of(maps.begin(), maps.end(), [](const ProcMapsEntry& e) { + return e.filename == "[vsyscall]"; + }); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/proc_util.h b/test/util/proc_util.h new file mode 100644 index 000000000..f8021d92e --- /dev/null +++ b/test/util/proc_util.h @@ -0,0 +1,150 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_PROC_UTIL_H_ +#define GVISOR_TEST_UTIL_PROC_UTIL_H_ + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { + +// ProcMapsEntry contains the data from a single line in /proc//maps. +struct ProcMapsEntry { + uint64_t start; + uint64_t end; + bool readable; + bool writable; + bool executable; + bool priv; + uint64_t offset; + int major; + int minor; + int64_t inode; + std::string filename; +}; + +// Parses a ProcMaps line or returns an error. +PosixErrorOr ParseProcMapsLine(absl::string_view line); +PosixErrorOr> ParseProcMaps( + absl::string_view contents); + +// Returns true if vsyscall (emmulation or not) is enabled. +PosixErrorOr IsVsyscallEnabled(); + +// Printer for ProcMapsEntry. +inline std::ostream& operator<<(std::ostream& os, const ProcMapsEntry& entry) { + std::string str = + absl::StrCat(absl::Hex(entry.start, absl::PadSpec::kZeroPad8), "-", + absl::Hex(entry.end, absl::PadSpec::kZeroPad8), " "); + + absl::StrAppend(&str, entry.readable ? "r" : "-"); + absl::StrAppend(&str, entry.writable ? "w" : "-"); + absl::StrAppend(&str, entry.executable ? "x" : "-"); + absl::StrAppend(&str, entry.priv ? "p" : "s"); + + absl::StrAppend(&str, " ", absl::Hex(entry.offset, absl::PadSpec::kZeroPad8), + " ", absl::Hex(entry.major, absl::PadSpec::kZeroPad2), ":", + absl::Hex(entry.minor, absl::PadSpec::kZeroPad2), " ", + entry.inode); + if (absl::string_view(entry.filename) != "") { + // Pad to column 74 + int pad = 73 - str.length(); + if (pad > 0) { + absl::StrAppend(&str, std::string(pad, ' ')); + } + absl::StrAppend(&str, entry.filename); + } + os << str; + return os; +} + +// Printer for std::vector. +inline std::ostream& operator<<(std::ostream& os, + const std::vector& vec) { + for (unsigned int i = 0; i < vec.size(); i++) { + os << vec[i]; + if (i != vec.size() - 1) { + os << "\n"; + } + } + return os; +} + +// GMock printer for std::vector. +inline void PrintTo(const std::vector& vec, std::ostream* os) { + *os << vec; +} + +// Checks that /proc/pid/maps contains all of the passed mappings. +// +// The major, minor, and inode fields are ignored. +MATCHER_P(ContainsMappings, mappings, + "contains mappings:\n" + ::testing::PrintToString(mappings)) { + auto contents_or = GetContents(absl::StrCat("/proc/", arg, "/maps")); + if (!contents_or.ok()) { + *result_listener << "Unable to read mappings: " + << contents_or.error().ToString(); + return false; + } + + auto maps_or = ParseProcMaps(contents_or.ValueOrDie()); + if (!maps_or.ok()) { + *result_listener << "Unable to parse mappings: " + << maps_or.error().ToString(); + return false; + } + + auto maps = std::move(maps_or.ValueOrDie()); + + // Does maps contain all elements in mappings? The comparator ignores + // the major, minor, and inode fields. + bool all_present = true; + std::for_each(mappings.begin(), mappings.end(), [&](const ProcMapsEntry& e1) { + auto it = + std::find_if(maps.begin(), maps.end(), [&e1](const ProcMapsEntry& e2) { + return e1.start == e2.start && e1.end == e2.end && + e1.readable == e2.readable && e1.writable == e2.writable && + e1.executable == e2.executable && e1.priv == e2.priv && + e1.offset == e2.offset && e1.filename == e2.filename; + }); + if (it == maps.end()) { + // It wasn't found. + if (all_present) { + // We will output the message once and then a line for each mapping + // that wasn't found. + all_present = false; + *result_listener << "Got mappings:\n" + << maps << "\nThat were missing:\n"; + } + *result_listener << e1 << "\n"; + } + }); + + return all_present; +} + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_PROC_UTIL_H_ diff --git a/test/util/save_util.cc b/test/util/save_util.cc new file mode 100644 index 000000000..71f4078a7 --- /dev/null +++ b/test/util/save_util.cc @@ -0,0 +1,59 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/save_util.h" + +#include +#include +#include +#include +#include +#include + +#define GVISOR_COOPERATIVE_SAVE_TEST "GVISOR_COOPERATIVE_SAVE_TEST" + +namespace gvisor { +namespace testing { +namespace { + +bool CooperativeSaveEnabled() { + static bool enabled = getenv(GVISOR_COOPERATIVE_SAVE_TEST) != nullptr; + return enabled; +} + +std::atomic save_disable; + +} // namespace + +DisableSave::DisableSave() { save_disable++; } + +DisableSave::~DisableSave() { reset(); } + +void DisableSave::reset() { + if (!reset_) { + reset_ = true; + save_disable--; + } +} + +void MaybeSave() { + if (CooperativeSaveEnabled() && !save_disable.load()) { + int orig_errno = errno; + syscall(SYS_create_module, nullptr, 0); + errno = orig_errno; + } +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/save_util.h b/test/util/save_util.h new file mode 100644 index 000000000..919e4af3d --- /dev/null +++ b/test/util/save_util.h @@ -0,0 +1,47 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_SAVE_UTIL_H_ +#define GVISOR_TEST_UTIL_SAVE_UTIL_H_ + +namespace gvisor { +namespace testing { +// Disable save prevents saving while the given function executes. +// +// This lasts the duration of the object, unless reset is called. +class DisableSave { + public: + DisableSave(); + ~DisableSave(); + DisableSave(DisableSave const&) = delete; + DisableSave(DisableSave&&) = delete; + DisableSave& operator=(DisableSave const&) = delete; + DisableSave& operator=(DisableSave&&) = delete; + + // reset allows saves to continue, and is called implicitly by the destructor. + // It may be called multiple times safely, but is not thread-safe. + void reset(); + + private: + bool reset_ = false; +}; + +// May perform a co-operative save cycle. +// +// errno is guaranteed to be preserved. +void MaybeSave(); +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_SAVE_UTIL_H_ diff --git a/test/util/signal_util.cc b/test/util/signal_util.cc new file mode 100644 index 000000000..3e2df32a6 --- /dev/null +++ b/test/util/signal_util.cc @@ -0,0 +1,103 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/signal_util.h" + +#include +#include + +#include "gtest/gtest.h" +#include "test/util/cleanup.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace { + +struct Range { + int start; + int end; +}; + +// Format a Range as "start-end" or "start" for single value Ranges. +static ::std::ostream& operator<<(::std::ostream& os, const Range& range) { + if (range.end > range.start) { + return os << range.start << '-' << range.end; + } + + return os << range.start; +} + +} // namespace + +// Format a sigset_t as a comma separated list of numeric ranges. +// Empty sigset: [] +// Full sigset: [1-31,34-64] +::std::ostream& operator<<(::std::ostream& os, const sigset_t& sigset) { + const char* delim = ""; + Range range = {0, 0}; + + os << '['; + + for (int sig = 1; sig <= gvisor::testing::kMaxSignal; ++sig) { + if (sigismember(&sigset, sig)) { + if (range.start) { + range.end = sig; + } else { + range.start = sig; + range.end = sig; + } + } else if (range.start) { + os << delim << range; + delim = ","; + range.start = 0; + range.end = 0; + } + } + + if (range.start) { + os << delim << range; + } + + return os << ']'; +} + +namespace gvisor { +namespace testing { + +PosixErrorOr ScopedSigaction(int sig, struct sigaction const& sa) { + struct sigaction old_sa; + int rc = sigaction(sig, &sa, &old_sa); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "sigaction failed"); + } + return Cleanup([sig, old_sa] { + EXPECT_THAT(sigaction(sig, &old_sa, nullptr), SyscallSucceeds()); + }); +} + +PosixErrorOr ScopedSignalMask(int how, sigset_t const& set) { + sigset_t old; + int rc = sigprocmask(how, &set, &old); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "sigprocmask failed"); + } + return Cleanup([old] { + EXPECT_THAT(sigprocmask(SIG_SETMASK, &old, nullptr), SyscallSucceeds()); + }); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/signal_util.h b/test/util/signal_util.h new file mode 100644 index 000000000..f58f4c6c4 --- /dev/null +++ b/test/util/signal_util.h @@ -0,0 +1,92 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ +#define GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/util/cleanup.h" +#include "test/util/posix_error.h" + +// Format a sigset_t as a comma separated list of numeric ranges. +::std::ostream& operator<<(::std::ostream& os, const sigset_t& sigset); + +namespace gvisor { +namespace testing { + +// The maximum signal number. +static constexpr int kMaxSignal = 64; + +// Wrapper for the tgkill(2) syscall, which glibc does not provide. +inline int tgkill(pid_t tgid, pid_t tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); +} + +// Installs the passed sigaction and returns a cleanup function to restore the +// previous handler when it goes out of scope. +PosixErrorOr ScopedSigaction(int sig, struct sigaction const& sa); + +// Updates the signal mask as per sigprocmask(2) and returns a cleanup function +// to restore the previous signal mask when it goes out of scope. +PosixErrorOr ScopedSignalMask(int how, sigset_t const& set); + +// ScopedSignalMask variant that creates a mask of the single signal 'sig'. +inline PosixErrorOr ScopedSignalMask(int how, int sig) { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, sig); + return ScopedSignalMask(how, set); +} + +// Asserts equality of two sigset_t values. +MATCHER_P(EqualsSigset, value, "equals " + ::testing::PrintToString(value)) { + for (int sig = 1; sig <= kMaxSignal; ++sig) { + if (sigismember(&arg, sig) != sigismember(&value, sig)) { + return false; + } + } + return true; +} + +#ifdef __x86_64__ +// Fault can be used to generate a synchronous SIGSEGV. +// +// This fault can be fixed up in a handler via fixup, below. +inline void Fault() { + // Zero and dereference %ax. + asm("movabs $0, %%rax\r\n" + "mov 0(%%rax), %%rax\r\n" + : + : + : "ax"); +} + +// FixupFault fixes up a fault generated by fault, above. +inline void FixupFault(ucontext* ctx) { + // Skip the bad instruction above. + // + // The encoding is 0x48 0xab 0x00. + ctx->uc_mcontext.gregs[REG_RIP] += 3; +} +#endif + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ diff --git a/test/util/temp_path.cc b/test/util/temp_path.cc new file mode 100644 index 000000000..e45909655 --- /dev/null +++ b/test/util/temp_path.cc @@ -0,0 +1,157 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/temp_path.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +std::atomic global_temp_file_number = ATOMIC_VAR_INIT(1); + +// Return a new temp filename, intended to be unique system-wide. +// +// The global file number helps maintain file naming consistency across +// different runs of a test. +// +// The timestamp is necessary because the test infrastructure invokes each +// test case in a separate process (resetting global_temp_file_number) and +// potentially in parallel, which allows for races between selecting and using a +// name. +std::string NextTempBasename() { + return absl::StrCat("gvisor_test_temp_", global_temp_file_number++, "_", + absl::ToUnixNanos(absl::Now())); +} + +void TryDeleteRecursively(std::string const& path) { + if (!path.empty()) { + int undeleted_dirs = 0; + int undeleted_files = 0; + auto status = RecursivelyDelete(path, &undeleted_dirs, &undeleted_files); + if (undeleted_dirs || undeleted_files || !status.ok()) { + LOG(WARNING) << path << ": failed to delete " << undeleted_dirs + << " directories and " << undeleted_files + << " files: " << status; + } + } +} + +} // namespace + +constexpr mode_t TempPath::kDefaultFileMode; +constexpr mode_t TempPath::kDefaultDirMode; + +std::string NewTempAbsPathInDir(absl::string_view const dir) { + return JoinPath(dir, NextTempBasename()); +} + +std::string NewTempAbsPath() { return NewTempAbsPathInDir(GetAbsoluteTestTmpdir()); } + +std::string NewTempRelPath() { return NextTempBasename(); } + +std::string GetAbsoluteTestTmpdir() { + char* env_tmpdir = getenv("TEST_TMPDIR"); + std::string tmp_dir = env_tmpdir != nullptr ? std::string(env_tmpdir) : "/tmp"; + return MakeAbsolute(tmp_dir, "").ValueOrDie(); +} + +PosixErrorOr TempPath::CreateFileWith(absl::string_view const parent, + absl::string_view const content, + mode_t const mode) { + return CreateIn(parent, [=](absl::string_view path) -> PosixError { + // SetContents will call open(O_WRONLY) with the given mode. If the + // mode is not user-writable, save/restore cannot preserve the fd. Hence + // the little permission dance that's done here. + auto res = CreateWithContents(path, content, mode | 0200); + RETURN_IF_ERRNO(res); + + return Chmod(path, mode); + }); +} + +PosixErrorOr TempPath::CreateDirWith(absl::string_view const parent, + mode_t const mode) { + return CreateIn(parent, + [=](absl::string_view path) { return Mkdir(path, mode); }); +} + +PosixErrorOr TempPath::CreateSymlinkTo(absl::string_view const parent, + std::string const& dest) { + return CreateIn(parent, [=](absl::string_view path) { + int ret = symlink(dest.c_str(), std::string(path).c_str()); + if (ret != 0) { + return PosixError(errno, "symlink failed"); + } + return NoError(); + }); +} + +PosixErrorOr TempPath::CreateFileIn(absl::string_view const parent) { + return TempPath::CreateFileWith(parent, absl::string_view(), + kDefaultFileMode); +} + +PosixErrorOr TempPath::CreateDirIn(absl::string_view const parent) { + return TempPath::CreateDirWith(parent, kDefaultDirMode); +} + +PosixErrorOr TempPath::CreateFileMode(mode_t mode) { + return TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), absl::string_view(), + mode); +} + +PosixErrorOr TempPath::CreateFile() { + return TempPath::CreateFileIn(GetAbsoluteTestTmpdir()); +} + +PosixErrorOr TempPath::CreateDir() { + return TempPath::CreateDirIn(GetAbsoluteTestTmpdir()); +} + +TempPath::~TempPath() { TryDeleteRecursively(path_); } + +TempPath::TempPath(TempPath&& orig) { reset(orig.release()); } + +TempPath& TempPath::operator=(TempPath&& orig) { + reset(orig.release()); + return *this; +} + +std::string TempPath::reset(std::string newpath) { + std::string path = path_; + TryDeleteRecursively(path_); + path_ = std::move(newpath); + return path; +} + +std::string TempPath::release() { + std::string path = path_; + path_ = std::string(); + return path; +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/temp_path.h b/test/util/temp_path.h new file mode 100644 index 000000000..33eb6a72c --- /dev/null +++ b/test/util/temp_path.h @@ -0,0 +1,134 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_TEMP_PATH_H_ +#define GVISOR_TEST_UTIL_TEMP_PATH_H_ + +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { + +// Returns an absolute path for a file in `dir` that does not yet exist. +// Distinct calls to NewTempAbsPathInDir from the same process, even from +// multiple threads, are guaranteed to return different paths. Distinct calls to +// NewTempAbsPathInDir from different processes are not synchronized. +std::string NewTempAbsPathInDir(absl::string_view base); + +// Like NewTempAbsPathInDir, but the returned path is in the test's temporary +// directory, as provided by the testing framework. +std::string NewTempAbsPath(); + +// Like NewTempAbsPathInDir, but the returned path is relative (to the current +// working directory). +std::string NewTempRelPath(); + +// Returns the absolute path for the test temp dir. +std::string GetAbsoluteTestTmpdir(); + +// Represents a temporary file or directory. +class TempPath { + public: + // Default creation mode for files. + static constexpr mode_t kDefaultFileMode = 0644; + + // Default creation mode for directories. + static constexpr mode_t kDefaultDirMode = 0755; + + // Creates a temporary file in directory `parent` with mode `mode` and + // contents `content`. + static PosixErrorOr CreateFileWith(absl::string_view parent, + absl::string_view content, + mode_t mode); + + // Creates an empty temporary subdirectory in directory `parent` with mode + // `mode`. + static PosixErrorOr CreateDirWith(absl::string_view parent, + mode_t mode); + + // Creates a temporary symlink in directory `parent` to destination `dest`. + static PosixErrorOr CreateSymlinkTo(absl::string_view parent, + std::string const& dest); + + // Creates an empty temporary file in directory `parent` with mode + // kDefaultFileMode. + static PosixErrorOr CreateFileIn(absl::string_view parent); + + // Creates an empty temporary subdirectory in directory `parent` with mode + // kDefaultDirMode. + static PosixErrorOr CreateDirIn(absl::string_view parent); + + // Creates an empty temporary file in the test's temporary directory with mode + // `mode`. + static PosixErrorOr CreateFileMode(mode_t mode); + + // Creates an empty temporary file in the test's temporary directory with + // mode kDefaultFileMode. + static PosixErrorOr CreateFile(); + + // Creates an empty temporary subdirectory in the test's temporary directory + // with mode kDefaultDirMode. + static PosixErrorOr CreateDir(); + + // Constructs a TempPath that represents nothing. + TempPath() = default; + + // Constructs a TempPath that represents the given path, which will be deleted + // when the TempPath is destroyed. + explicit TempPath(std::string path) : path_(std::move(path)) {} + + // Attempts to delete the represented temporary file or directory (in the + // latter case, also attempts to delete its contents). + ~TempPath(); + + // Attempts to delete the represented temporary file or directory, then + // transfers ownership of the path represented by orig to this TempPath. + TempPath(TempPath&& orig); + TempPath& operator=(TempPath&& orig); + + // Changes the path this TempPath represents. If the TempPath already + // represented a path, deletes and returns that path. Otherwise returns the + // empty std::string. + std::string reset(std::string newpath); + std::string reset() { return reset(""); } + + // Forgets and returns the path this TempPath represents. The path is not + // deleted. + std::string release(); + + // Returns the path this TempPath represents. + std::string path() const { return path_; } + + private: + template + static PosixErrorOr CreateIn(absl::string_view const parent, + F const& f) { + std::string path = NewTempAbsPathInDir(parent); + RETURN_IF_ERRNO(f(path)); + return TempPath(std::move(path)); + } + + std::string path_; +}; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_TEMP_PATH_H_ diff --git a/test/util/test_main.cc b/test/util/test_main.cc new file mode 100644 index 000000000..4c6b5e860 --- /dev/null +++ b/test/util/test_main.cc @@ -0,0 +1,20 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/test_util.h" + +int main(int argc, char** argv) { + gvisor::testing::TestInit(&argc, &argv); + return RUN_ALL_TESTS(); +} diff --git a/test/util/test_util.cc b/test/util/test_util.cc new file mode 100644 index 000000000..7b40260d1 --- /dev/null +++ b/test/util/test_util.cc @@ -0,0 +1,248 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/test_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "absl/base/attributes.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/time/time.h" +#include "test/util/fs_util.h" +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { + +#define TEST_ON_GVISOR "TEST_ON_GVISOR" + +bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; } + +Platform GvisorPlatform() { + // Set by runner.go. + char* env = getenv(TEST_ON_GVISOR); + if (!env) { + return Platform::kNative; + } + if (strcmp(env, "ptrace") == 0) { + return Platform::kPtrace; + } + if (strcmp(env, "kvm") == 0) { + return Platform::kKVM; + } + LOG(FATAL) << "unknown platform " << env; + __builtin_unreachable(); +} + +// Inline cpuid instruction. Preserve %ebx/%rbx register. In PIC compilations +// %ebx contains the address of the global offset table. %rbx is occasionally +// used to address stack variables in presence of dynamic allocas. +#if defined(__x86_64__) +#define GETCPUID(a, b, c, d, a_inp, c_inp) \ + asm("mov %%rbx, %%rdi\n" \ + "cpuid\n" \ + "xchg %%rdi, %%rbx\n" \ + : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \ + : "a"(a_inp), "2"(c_inp)) +#endif // defined(__x86_64__) + +CPUVendor GetCPUVendor() { + uint32_t eax, ebx, ecx, edx; + std::string vendor_str; + // Get vendor std::string (issue CPUID with eax = 0) + GETCPUID(eax, ebx, ecx, edx, 0, 0); + vendor_str.append(reinterpret_cast(&ebx), 4); + vendor_str.append(reinterpret_cast(&edx), 4); + vendor_str.append(reinterpret_cast(&ecx), 4); + if (vendor_str == "GenuineIntel") { + return CPUVendor::kIntel; + } else if (vendor_str == "AuthenticAMD") { + return CPUVendor::kAMD; + } + return CPUVendor::kUnknownVendor; +} + +bool operator==(const KernelVersion& first, const KernelVersion& second) { + return first.major == second.major && first.minor == second.minor && + first.micro == second.micro; +} + +PosixErrorOr ParseKernelVersion(absl::string_view vers_str) { + KernelVersion version = {}; + std::vector values = absl::StrSplit(vers_str, absl::ByAnyChar(".-")); + if (values.size() == 2) { + ASSIGN_OR_RETURN_ERRNO(version.major, Atoi(values[0])); + ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi(values[1])); + return version; + } else if (values.size() >= 3) { + ASSIGN_OR_RETURN_ERRNO(version.major, Atoi(values[0])); + ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi(values[1])); + ASSIGN_OR_RETURN_ERRNO(version.micro, Atoi(values[2])); + return version; + } + return PosixError(EINVAL, absl::StrCat("Unknown kernel release: ", vers_str)); +} + +PosixErrorOr GetKernelVersion() { + utsname buf; + RETURN_ERROR_IF_SYSCALL_FAIL(uname(&buf)); + return ParseKernelVersion(buf.release); +} + +void SetupGvisorDeathTest() { +} + +std::string CPUSetToString(const cpu_set_t& set, size_t cpus) { + std::string str = "cpuset["; + for (unsigned int n = 0; n < cpus; n++) { + if (CPU_ISSET(n, &set)) { + if (n != 0) { + absl::StrAppend(&str, " "); + } + absl::StrAppend(&str, n); + } + } + absl::StrAppend(&str, "]"); + return str; +} + +// An overloaded operator<< makes it easy to dump the value of an OpenFd. +std::ostream& operator<<(std::ostream& out, OpenFd const& ofd) { + out << ofd.fd << " -> " << ofd.link; + return out; +} + +// An overloaded operator<< makes it easy to dump a vector of OpenFDs. +std::ostream& operator<<(std::ostream& out, std::vector const& v) { + for (const auto& ofd : v) { + out << ofd << std::endl; + } + return out; +} + +PosixErrorOr> GetOpenFDs() { + // Get the results from /proc/self/fd. + ASSIGN_OR_RETURN_ERRNO(auto dir_list, + ListDir("/proc/self/fd", /*skipdots=*/true)); + + std::vector ret_fds; + for (const auto& str_fd : dir_list) { + OpenFd open_fd = {}; + ASSIGN_OR_RETURN_ERRNO(open_fd.fd, Atoi(str_fd)); + std::string path = absl::StrCat("/proc/self/fd/", open_fd.fd); + + // Resolve the link. + char buf[PATH_MAX] = {}; + int ret = readlink(path.c_str(), buf, sizeof(buf)); + if (ret < 0) { + if (errno == ENOENT) { + // The FD may have been closed, let's be resilient. + continue; + } + + return PosixError( + errno, absl::StrCat("readlink of ", path, " returned errno ", errno)); + } + open_fd.link = std::string(buf, ret); + ret_fds.emplace_back(std::move(open_fd)); + } + return ret_fds; +} + +PosixErrorOr Links(const std::string& path) { + struct stat st; + if (stat(path.c_str(), &st)) { + return PosixError(errno, absl::StrCat("Failed to stat ", path)); + } + return static_cast(st.st_nlink); +} + +void RandomizeBuffer(void* buffer, size_t len) { + struct timespec ts = {}; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint32_t seed = static_cast(ts.tv_nsec); + char* const buf = static_cast(buffer); + for (size_t i = 0; i < len; i++) { + buf[i] = rand_r(&seed) % 255; + } +} + +std::vector> GenerateIovecs(uint64_t total_size, + void* buf, + size_t buflen) { + std::vector> result; + for (uint64_t offset = 0; offset < total_size;) { + auto& iovec_array = *result.emplace(result.end()); + + for (; offset < total_size && iovec_array.size() < IOV_MAX; + offset += buflen) { + struct iovec iov = {}; + iov.iov_base = buf; + iov.iov_len = std::min(total_size - offset, buflen); + iovec_array.push_back(iov); + } + } + + return result; +} + +void SleepSafe(absl::Duration duration) { + if (duration == absl::ZeroDuration()) { + return; + } + + struct timespec ts = absl::ToTimespec(duration); + int ret; + while (1) { + ret = syscall(__NR_nanosleep, &ts, &ts); + if (ret == 0 || (ret <= 0 && errno != EINTR)) { + break; + } + } +} + +uint64_t Megabytes(uint64_t n) { + // Overflow check, upper 20 bits in n shouldn't be set. + CHECK(!(0xfffff00000000000 & n)); + return n << 20; +} + +bool Equivalent(uint64_t current, uint64_t target, double tolerance) { + auto abs_diff = target > current ? target - current : current - target; + return abs_diff <= static_cast(tolerance * target); +} +void TestInit(int* argc, char*** argv) { + ::testing::InitGoogleTest(argc, *argv); + ::gflags::ParseCommandLineFlags(argc, argv, true); + + // Always mask SIGPIPE as it's common and tests aren't expected to handle it. + struct sigaction sa = {}; + sa.sa_handler = SIG_IGN; + TEST_CHECK(sigaction(SIGPIPE, &sa, nullptr) == 0); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/test_util.h b/test/util/test_util.h new file mode 100644 index 000000000..2a7609e5c --- /dev/null +++ b/test/util/test_util.h @@ -0,0 +1,794 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Utilities for syscall testing. +// +// Initialization +// ============== +// +// Prior to calling RUN_ALL_TESTS, all tests must use TestInit(&argc, &argv). +// See the TestInit function for exact side-effects and semantics. +// +// Configuration +// ============= +// +// IsRunningOnGvisor returns true if the test is known to be running on gVisor. +// GvisorPlatform can be used to get more detail: +// +// switch (GvisorPlatform()) { +// case Platform::kNative: +// case Platform::kGvisor: +// EXPECT_THAT(mmap(...), SyscallSucceeds()); +// break; +// case Platform::kPtrace: +// EXPECT_THAT(mmap(...), SyscallFailsWithErrno(ENOSYS)); +// break; +// } +// +// Matchers +// ======== +// +// ElementOf(xs) matches if the matched value is equal to an element of the +// container xs. Example: +// +// // PASS +// EXPECT_THAT(1, ElementOf({0, 1, 2})); +// +// // FAIL +// // Value of: 3 +// // Expected: one of {0, 1, 2} +// // Actual: 3 +// EXPECT_THAT(3, ElementOf({0, 1, 2})); +// +// SyscallSucceeds() matches if the syscall is successful. A successful syscall +// is defined by either a return value not equal to -1, or a return value of -1 +// with an errno of 0 (which is a possible successful return for e.g. +// PTRACE_PEEK). Example: +// +// // PASS +// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallSucceeds()); +// +// // FAIL +// // Value of: open("/", O_RDWR) +// // Expected: not -1 (success) +// // Actual: -1 (of type int), with errno 21 (Is a directory) +// EXPECT_THAT(open("/", O_RDWR), SyscallSucceeds()); +// +// SyscallSucceedsWithValue(m) matches if the syscall is successful, and the +// value also matches m. Example: +// +// // PASS +// EXPECT_THAT(read(4, buf, 8192), SyscallSucceedsWithValue(8192)); +// +// // FAIL +// // Value of: read(-1, buf, 8192) +// // Expected: is equal to 8192 +// // Actual: -1 (of type long), with errno 9 (Bad file number) +// EXPECT_THAT(read(-1, buf, 8192), SyscallSucceedsWithValue(8192)); +// +// // FAIL +// // Value of: read(4, buf, 1) +// // Expected: is > 4096 +// // Actual: 1 (of type long) +// EXPECT_THAT(read(4, buf, 1), SyscallSucceedsWithValue(Gt(4096))); +// +// SyscallFails() matches if the syscall is unsuccessful. An unsuccessful +// syscall is defined by a return value of -1 with a non-zero errno. Example: +// +// // PASS +// EXPECT_THAT(open("/", O_RDWR), SyscallFails()); +// +// // FAIL +// // Value of: open("/dev/null", O_RDONLY) +// // Expected: -1 (failure) +// // Actual: 0 (of type int) +// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallFails()); +// +// SyscallFailsWithErrno(m) matches if the syscall is unsuccessful, and errno +// matches m. Example: +// +// // PASS +// EXPECT_THAT(open("/", O_RDWR), SyscallFailsWithErrno(EISDIR)); +// +// // PASS +// EXPECT_THAT(open("/etc/passwd", O_RDWR | O_DIRECTORY), +// SyscallFailsWithErrno(AnyOf(EACCES, ENOTDIR))); +// +// // FAIL +// // Value of: open("/dev/null", O_RDONLY) +// // Expected: -1 (failure) with errno 21 (Is a directory) +// // Actual: 0 (of type int) +// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallFailsWithErrno(EISDIR)); +// +// // FAIL +// // Value of: open("/", O_RDWR) +// // Expected: -1 (failure) with errno 22 (Invalid argument) +// // Actual: -1 (of type int), failure, but with errno 21 (Is a directory) +// EXPECT_THAT(open("/", O_RDWR), SyscallFailsWithErrno(EINVAL)); +// +// Because the syscall matchers encode save/restore functionality, their meaning +// should not be inverted via Not. That is, AnyOf(SyscallSucceedsWithValue(1), +// SyscallSucceedsWithValue(2)) is permitted, but not +// Not(SyscallFailsWithErrno(EPERM)). +// +// Syscalls +// ======== +// +// RetryEINTR wraps a function that returns -1 and sets errno on failure +// to be automatically retried when EINTR occurs. Example: +// +// auto rv = RetryEINTR(waitpid)(pid, &status, 0); +// +// ReadFd/WriteFd/PreadFd/PwriteFd are interface-compatible wrappers around the +// read/write/pread/pwrite syscalls to handle both EINTR and partial +// reads/writes. Example: +// +// EXPECT_THAT(ReadFd(fd, &buf, size), SyscallSucceedsWithValue(size)); +// +// General Utilities +// ================= +// +// ApplyVec(f, xs) returns a vector containing the result of applying function +// `f` to each value in `xs`. +// +// AllBitwiseCombinations takes a variadic number of ranges containing integers +// and returns a vector containing every integer that can be formed by ORing +// together exactly one integer from each list. List is an alias for +// std::initializer_list that makes AllBitwiseCombinations more ergonomic to +// use with list literals (initializer lists do not otherwise participate in +// template argument deduction). Example: +// +// EXPECT_THAT( +// AllBitwiseCombinations( +// List{SOCK_DGRAM, SOCK_STREAM}, +// List{0, SOCK_NONBLOCK}), +// Contains({SOCK_DGRAM, SOCK_STREAM, SOCK_DGRAM | SOCK_NONBLOCK, +// SOCK_STREAM | SOCK_NONBLOCK})); +// +// VecCat takes a variadic number of containers and returns a vector containing +// the concatenated contents. +// +// VecAppend takes an initial container and a variadic number of containers and +// appends each to the initial container. +// +// RandomizeBuffer will use MTRandom to fill the given buffer with random bytes. +// +// GenerateIovecs will return the smallest number of iovec arrays for writing a +// given total number of bytes to a file, each iovec array size up to IOV_MAX, +// each iovec in each array pointing to the same buffer. + +#ifndef GVISOR_TEST_UTIL_TEST_UTIL_H_ +#define GVISOR_TEST_UTIL_TEST_UTIL_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include // NOLINT: using std::thread::hardware_concurrency(). +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "test/util/fs_util.h" +#include "test/util/logging.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" + +namespace gvisor { +namespace testing { + +// TestInit must be called prior to RUN_ALL_TESTS. +// +// This parses all arguments and adjusts argc and argv appropriately. +// +// TestInit may create background threads. +void TestInit(int* argc, char*** argv); + +// SKIP_IF may be used to skip a test case. +// +// These cases are still emitted, but a SKIPPED line will appear. +#define SKIP_IF(expr) \ + do { \ + if (expr) { \ + std::cout << "\033[0;33m[ SKIPPED ]\033[m => " << #expr << std::endl; \ + return; \ + } \ + } while (0) + +#define SKIP_BEFORE_KERNEL(maj, min) \ + do { \ + auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); \ + SKIP_IF(version.major < (maj) || \ + (version.major == (maj) && version.minor < (min))); \ + } while (0) + +enum class Platform { + kNative, + kKVM, + kPtrace, +}; +bool IsRunningOnGvisor(); +Platform GvisorPlatform(); + +void SetupGvisorDeathTest(); + +struct KernelVersion { + int major; + int minor; + int micro; +}; + +bool operator==(const KernelVersion& first, const KernelVersion& second); + +PosixErrorOr ParseKernelVersion(absl::string_view vers_string); +PosixErrorOr GetKernelVersion(); + +static const size_t kPageSize = sysconf(_SC_PAGESIZE); + +enum class CPUVendor { kIntel, kAMD, kUnknownVendor }; + +CPUVendor GetCPUVendor(); + +inline int NumCPUs() { return std::thread::hardware_concurrency(); } + +// Converts cpu_set_t to a std::string for easy examination. +std::string CPUSetToString(const cpu_set_t& set, size_t cpus = CPU_SETSIZE); + +struct OpenFd { + // fd is the open file descriptor number. + int fd = -1; + + // link is the resolution of the symbolic link. + std::string link; +}; + +// Make it easier to log OpenFds to error streams. +std::ostream& operator<<(std::ostream& out, std::vector const& v); +std::ostream& operator<<(std::ostream& out, OpenFd const& ofd); + +// Gets a detailed list of open fds for this process. +PosixErrorOr> GetOpenFDs(); + +// Returns the number of hard links to a path. +PosixErrorOr Links(const std::string& path); + +namespace internal { + +inline std::string ErrnoWithMessage(int const errnum) { + char buf[1024] = {}; + const char* str = strerror_r(errnum, buf, sizeof(buf)); + if (str == nullptr || str[0] == '\0') { + snprintf(buf, sizeof(buf), "Unknown error %d", errnum); + str = buf; + } + return absl::StrCat(errnum, " (", str, ")"); +} + +template +class ElementOfMatcher { + public: + explicit ElementOfMatcher(Container container) + : container_(::std::move(container)) {} + + template + bool MatchAndExplain(T const& rv, + ::testing::MatchResultListener* const listener) const { + using std::count; + return count(container_.begin(), container_.end(), rv) != 0; + } + + void DescribeTo(::std::ostream* const os) const { + *os << "one of {"; + char const* sep = ""; + for (auto const& elem : container_) { + *os << sep << elem; + sep = ", "; + } + *os << "}"; + } + + void DescribeNegationTo(::std::ostream* const os) const { + *os << "none of {"; + char const* sep = ""; + for (auto const& elem : container_) { + *os << sep << elem; + sep = ", "; + } + *os << "}"; + } + + private: + Container const container_; +}; + +template +class SyscallSuccessMatcher { + public: + explicit SyscallSuccessMatcher(E expected) + : expected_(::std::move(expected)) {} + + template + operator ::testing::Matcher() const { + // E is one of three things: + // - T, or a type losslessly and implicitly convertible to T. + // - A monomorphic Matcher. + // - A polymorphic matcher. + // SafeMatcherCast handles any of the above correctly. + // + // Similarly, gMock will invoke this conversion operator to obtain a + // monomorphic matcher (this is how polymorphic matchers are implemented). + return ::testing::MakeMatcher( + new Impl(::testing::SafeMatcherCast(expected_))); + } + + private: + template + class Impl : public ::testing::MatcherInterface { + public: + explicit Impl(::testing::Matcher matcher) + : matcher_(::std::move(matcher)) {} + + bool MatchAndExplain( + T const& rv, + ::testing::MatchResultListener* const listener) const override { + if (rv == static_cast(-1) && errno != 0) { + *listener << "with errno " << ErrnoWithMessage(errno); + return false; + } + bool match = matcher_.MatchAndExplain(rv, listener); + if (match) { + MaybeSave(); + } + return match; + } + + void DescribeTo(::std::ostream* const os) const override { + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* const os) const override { + matcher_.DescribeNegationTo(os); + } + + private: + ::testing::Matcher matcher_; + }; + + private: + E expected_; +}; + +// A polymorphic matcher equivalent to ::testing::internal::AnyMatcher, except +// not in namespace ::testing::internal, and describing SyscallSucceeds()'s +// match constraints (which are enforced by SyscallSuccessMatcher::Impl). +class AnySuccessValueMatcher { + public: + template + operator ::testing::Matcher() const { + return ::testing::MakeMatcher(new Impl()); + } + + private: + template + class Impl : public ::testing::MatcherInterface { + public: + bool MatchAndExplain( + T const& rv, + ::testing::MatchResultListener* const listener) const override { + return true; + } + + void DescribeTo(::std::ostream* const os) const override { + *os << "not -1 (success)"; + } + + void DescribeNegationTo(::std::ostream* const os) const override { + *os << "-1 (failure)"; + } + }; +}; + +class SyscallFailureMatcher { + public: + explicit SyscallFailureMatcher(::testing::Matcher errno_matcher) + : errno_matcher_(std::move(errno_matcher)) {} + + template + bool MatchAndExplain(T const& rv, + ::testing::MatchResultListener* const listener) const { + if (rv != static_cast(-1)) { + return false; + } + int actual_errno = errno; + *listener << "with errno " << ErrnoWithMessage(actual_errno); + bool match = errno_matcher_.MatchAndExplain(actual_errno, listener); + if (match) { + MaybeSave(); + } + return match; + } + + void DescribeTo(::std::ostream* const os) const { + *os << "-1 (failure), with errno "; + errno_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* const os) const { + *os << "not -1 (success), with errno "; + errno_matcher_.DescribeNegationTo(os); + } + + private: + ::testing::Matcher errno_matcher_; +}; + +class SpecificErrnoMatcher : public ::testing::MatcherInterface { + public: + explicit SpecificErrnoMatcher(int const expected) : expected_(expected) {} + + bool MatchAndExplain( + int const actual_errno, + ::testing::MatchResultListener* const listener) const override { + return actual_errno == expected_; + } + + void DescribeTo(::std::ostream* const os) const override { + *os << ErrnoWithMessage(expected_); + } + + void DescribeNegationTo(::std::ostream* const os) const override { + *os << "not " << ErrnoWithMessage(expected_); + } + + private: + int const expected_; +}; + +inline ::testing::Matcher SpecificErrno(int const expected) { + return ::testing::MakeMatcher(new SpecificErrnoMatcher(expected)); +} + +} // namespace internal + +template +inline ::testing::PolymorphicMatcher> +ElementOf(Container container) { + return ::testing::MakePolymorphicMatcher( + internal::ElementOfMatcher(::std::move(container))); +} + +template +inline ::testing::PolymorphicMatcher< + internal::ElementOfMatcher<::std::vector>> +ElementOf(::std::initializer_list elems) { + return ::testing::MakePolymorphicMatcher( + internal::ElementOfMatcher<::std::vector>(::std::vector(elems))); +} + +template +inline internal::SyscallSuccessMatcher SyscallSucceedsWithValue(E expected) { + return internal::SyscallSuccessMatcher(::std::move(expected)); +} + +inline internal::SyscallSuccessMatcher +SyscallSucceeds() { + return SyscallSucceedsWithValue( + ::gvisor::testing::internal::AnySuccessValueMatcher()); +} + +inline ::testing::PolymorphicMatcher +SyscallFailsWithErrno(::testing::Matcher expected) { + return ::testing::MakePolymorphicMatcher( + internal::SyscallFailureMatcher(::std::move(expected))); +} + +// Overload taking an int so that SyscallFailsWithErrno() uses +// internal::SpecificErrno (which stringifies the errno) rather than +// ::testing::Eq (which doesn't). +inline ::testing::PolymorphicMatcher +SyscallFailsWithErrno(int const expected) { + return SyscallFailsWithErrno(internal::SpecificErrno(expected)); +} + +inline ::testing::PolymorphicMatcher +SyscallFails() { + return SyscallFailsWithErrno(::testing::Gt(0)); +} + +// As of GCC 7.2, -Wall => -Wc++17-compat => -Wnoexcept-type generates an +// irrelevant, non-actionable warning about ABI compatibility when +// RetryEINTRImpl is constructed with a noexcept function, such as glibc's +// syscall(). See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80985. +#if defined(__GNUC__) && !defined(__clang__) && \ + (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnoexcept-type" +#endif + +namespace internal { + +template +struct RetryEINTRImpl { + F const f; + + explicit constexpr RetryEINTRImpl(F f) : f(std::move(f)) {} + + template + auto operator()(Args&&... args) const + -> decltype(f(std::forward(args)...)) { + while (true) { + errno = 0; + auto const ret = f(std::forward(args)...); + if (ret != -1 || errno != EINTR) { + return ret; + } + } + } +}; + +} // namespace internal + +template +constexpr internal::RetryEINTRImpl RetryEINTR(F&& f) { + return internal::RetryEINTRImpl(std::forward(f)); +} + +#if defined(__GNUC__) && !defined(__clang__) && \ + (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)) +#pragma GCC diagnostic pop +#endif + +namespace internal { + +template +ssize_t ApplyFileIoSyscall(F const& f, size_t const count) { + size_t completed = 0; + // `do ... while` because some callers actually want to make a syscall with a + // count of 0. + do { + auto const cur = RetryEINTR(f)(completed); + if (cur < 0) { + return cur; + } else if (cur == 0) { + break; + } + completed += cur; + } while (completed < count); + return completed; +} + +} // namespace internal + +inline ssize_t ReadFd(int fd, void* buf, size_t count) { + return internal::ApplyFileIoSyscall( + [&](size_t completed) { + return read(fd, static_cast(buf) + completed, count - completed); + }, + count); +} + +inline ssize_t WriteFd(int fd, void const* buf, size_t count) { + return internal::ApplyFileIoSyscall( + [&](size_t completed) { + return write(fd, static_cast(buf) + completed, + count - completed); + }, + count); +} + +inline ssize_t PreadFd(int fd, void* buf, size_t count, off_t offset) { + return internal::ApplyFileIoSyscall( + [&](size_t completed) { + return pread(fd, static_cast(buf) + completed, count - completed, + offset + completed); + }, + count); +} + +inline ssize_t PwriteFd(int fd, void const* buf, size_t count, off_t offset) { + return internal::ApplyFileIoSyscall( + [&](size_t completed) { + return pwrite(fd, static_cast(buf) + completed, + count - completed, offset + completed); + }, + count); +} + +template +using List = std::initializer_list; + +namespace internal { + +template +void AppendAllBitwiseCombinations(std::vector* combinations, T current) { + combinations->push_back(current); +} + +template +void AppendAllBitwiseCombinations(std::vector* combinations, T current, + Arg&& next, Args&&... rest) { + for (auto const option : next) { + AppendAllBitwiseCombinations(combinations, current | option, rest...); + } +} + +inline size_t CombinedSize(size_t accum) { return accum; } + +template +size_t CombinedSize(size_t accum, T const& x, Args&&... xs) { + return CombinedSize(accum + x.size(), std::forward(xs)...); +} + +// Base case: no more containers, so do nothing. +template +void DoMoveExtendContainer(T* c) {} + +// Append each container next to c. +template +void DoMoveExtendContainer(T* c, U&& next, Args&&... rest) { + std::move(std::begin(next), std::end(next), std::back_inserter(*c)); + DoMoveExtendContainer(c, std::forward(rest)...); +} + +} // namespace internal + +template +std::vector AllBitwiseCombinations() { + return std::vector(); +} + +template +std::vector AllBitwiseCombinations(Args&&... args) { + std::vector combinations; + internal::AppendAllBitwiseCombinations(&combinations, 0, args...); + return combinations; +} + +template +std::vector ApplyVec(F const& f, std::vector const& us) { + std::vector vec; + vec.reserve(us.size()); + for (auto const& u : us) { + vec.push_back(f(u)); + } + return vec; +} + +template +std::vector ApplyVecToVec(std::vector> const& fs, + std::vector const& us) { + std::vector vec; + vec.reserve(us.size() * fs.size()); + for (auto const& f : fs) { + for (auto const& u : us) { + vec.push_back(f(u)); + } + } + return vec; +} + +// Moves all elements from the containers `args` to the end of `c`. +template +void VecAppend(T* c, Args&&... args) { + c->reserve(internal::CombinedSize(c->size(), args...)); + internal::DoMoveExtendContainer(c, std::forward(args)...); +} + +// Returns a vector containing the concatenated contents of the containers +// `args`. +template +std::vector VecCat(Args&&... args) { + std::vector combined; + VecAppend(&combined, std::forward(args)...); + return combined; +} + +#define RETURN_ERROR_IF_SYSCALL_FAIL(syscall) \ + do { \ + if ((syscall) < 0 && errno != 0) { \ + return PosixError(errno, #syscall); \ + } \ + } while (false) + +// Fill the given buffer with random bytes. +void RandomizeBuffer(void* buffer, size_t len); + +template +inline PosixErrorOr Atoi(absl::string_view str) { + T ret; + if (!absl::SimpleAtoi(str, &ret)) { + return PosixError(EINVAL, "String not a number."); + } + return ret; +} + +inline PosixErrorOr AtoiBase(absl::string_view str, int base) { + if (base > 255 || base < 2) { + return PosixError(EINVAL, "Invalid Base"); + } + + uint64_t ret = 0; + if (!absl::numbers_internal::safe_strtou64_base(str, &ret, base)) { + return PosixError(EINVAL, "String not a number."); + } + + return ret; +} + +inline PosixErrorOr Atod(absl::string_view str) { + double ret; + if (!absl::SimpleAtod(str, &ret)) { + return PosixError(EINVAL, "String not a double type."); + } + return ret; +} + +inline PosixErrorOr Atof(absl::string_view str) { + float ret; + if (!absl::SimpleAtof(str, &ret)) { + return PosixError(EINVAL, "String not a float type."); + } + return ret; +} + +// Return the smallest number of iovec arrays that can be used to write +// "total_bytes" number of bytes, each iovec writing one "buf". +std::vector> GenerateIovecs(uint64_t total_size, + void* buf, size_t buflen); + +// Sleep for at least the specified duration. Avoids glibc. +void SleepSafe(absl::Duration duration); + +// Returns bytes in 'n' megabytes. Used for readability. +uint64_t Megabytes(uint64_t n); + +// Predicate for checking that a value is within some tolerance of another +// value. Returns true iff current is in the range [target * (1 - tolerance), +// target * (1 + tolerance)]. +bool Equivalent(uint64_t current, uint64_t target, double tolerance); + +// Matcher wrapping the Equivalent predicate. +MATCHER_P2(EquivalentWithin, target, tolerance, + std::string(negation ? "Isn't" : "Is") + + ::absl::StrFormat(" within %.2f%% of the target of %zd bytes", + tolerance * 100, target)) { + if (target == 0) { + *result_listener << ::absl::StreamFormat("difference of infinity%%"); + } else { + int64_t delta = static_cast(arg) - static_cast(target); + double delta_percent = + static_cast(delta) / static_cast(target) * 100; + *result_listener << ::absl::StreamFormat("difference of %.2f%%", + delta_percent); + } + return Equivalent(arg, target, tolerance); +} + +void TestInit(int* argc, char*** argv); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_TEST_UTIL_H_ diff --git a/test/util/test_util_test.cc b/test/util/test_util_test.cc new file mode 100644 index 000000000..5889651d1 --- /dev/null +++ b/test/util/test_util_test.cc @@ -0,0 +1,250 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/test_util.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::AnyOf; +using ::testing::Gt; +using ::testing::IsEmpty; +using ::testing::Lt; +using ::testing::Not; +using ::testing::TypedEq; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; + +namespace gvisor { +namespace testing { + +namespace { + +TEST(KernelVersionParsing, ValidateParsing) { + KernelVersion v = ASSERT_NO_ERRNO_AND_VALUE( + ParseKernelVersion("4.18.10-1foo2-amd64 baz blah")); + ASSERT_TRUE(v == KernelVersion({4, 18, 10})); + + v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-1foo2-amd64")); + ASSERT_TRUE(v == KernelVersion({4, 18, 10})); + + v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-14-amd64")); + ASSERT_TRUE(v == KernelVersion({4, 18, 10})); + + v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-amd64")); + ASSERT_TRUE(v == KernelVersion({4, 18, 10})); + + v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10")); + ASSERT_TRUE(v == KernelVersion({4, 18, 10})); + + v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.0.10")); + ASSERT_TRUE(v == KernelVersion({4, 0, 10})); + + v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.0")); + ASSERT_TRUE(v == KernelVersion({4, 0, 0})); + + ASSERT_THAT(ParseKernelVersion("4.a"), PosixErrorIs(EINVAL, ::testing::_)); + ASSERT_THAT(ParseKernelVersion("3"), PosixErrorIs(EINVAL, ::testing::_)); + ASSERT_THAT(ParseKernelVersion(""), PosixErrorIs(EINVAL, ::testing::_)); + ASSERT_THAT(ParseKernelVersion("version 3.3.10"), + PosixErrorIs(EINVAL, ::testing::_)); +} + +TEST(MatchersTest, SyscallSucceeds) { + EXPECT_THAT(0, SyscallSucceeds()); + EXPECT_THAT(0L, SyscallSucceeds()); + + errno = 0; + EXPECT_THAT(-1, SyscallSucceeds()); + EXPECT_THAT(-1L, SyscallSucceeds()); + + errno = ENOMEM; + EXPECT_THAT(-1, Not(SyscallSucceeds())); + EXPECT_THAT(-1L, Not(SyscallSucceeds())); +} + +TEST(MatchersTest, SyscallSucceedsWithValue) { + EXPECT_THAT(0, SyscallSucceedsWithValue(0)); + EXPECT_THAT(1, SyscallSucceedsWithValue(Lt(3))); + EXPECT_THAT(-1, Not(SyscallSucceedsWithValue(Lt(3)))); + EXPECT_THAT(4, Not(SyscallSucceedsWithValue(Lt(3)))); + + // Non-int -1 + EXPECT_THAT(-1L, Not(SyscallSucceedsWithValue(0))); + + // Non-int, truncates to -1 if converted to int, with expected value + EXPECT_THAT(0xffffffffL, SyscallSucceedsWithValue(0xffffffffL)); + + // Non-int, truncates to -1 if converted to int, with monomorphic matcher + EXPECT_THAT(0xffffffffL, + SyscallSucceedsWithValue(TypedEq(0xffffffffL))); + + // Non-int, truncates to -1 if converted to int, with polymorphic matcher + EXPECT_THAT(0xffffffffL, SyscallSucceedsWithValue(Gt(1))); +} + +TEST(MatchersTest, SyscallFails) { + EXPECT_THAT(0, Not(SyscallFails())); + EXPECT_THAT(0L, Not(SyscallFails())); + + errno = 0; + EXPECT_THAT(-1, Not(SyscallFails())); + EXPECT_THAT(-1L, Not(SyscallFails())); + + errno = ENOMEM; + EXPECT_THAT(-1, SyscallFails()); + EXPECT_THAT(-1L, SyscallFails()); +} + +TEST(MatchersTest, SyscallFailsWithErrno) { + EXPECT_THAT(0, Not(SyscallFailsWithErrno(EINVAL))); + EXPECT_THAT(0L, Not(SyscallFailsWithErrno(EINVAL))); + + errno = ENOMEM; + EXPECT_THAT(-1, Not(SyscallFailsWithErrno(EINVAL))); + EXPECT_THAT(-1L, Not(SyscallFailsWithErrno(EINVAL))); + + errno = EINVAL; + EXPECT_THAT(-1, SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(-1L, SyscallFailsWithErrno(EINVAL)); + + EXPECT_THAT(-1, SyscallFailsWithErrno(AnyOf(EINVAL, ENOMEM))); + EXPECT_THAT(-1L, SyscallFailsWithErrno(AnyOf(EINVAL, ENOMEM))); + + std::vector expected_errnos({EINVAL, ENOMEM}); + errno = ENOMEM; + EXPECT_THAT(-1, SyscallFailsWithErrno(ElementOf(expected_errnos))); + EXPECT_THAT(-1L, SyscallFailsWithErrno(ElementOf(expected_errnos))); +} + +TEST(AllBitwiseCombinationsTest, NoArguments) { + EXPECT_THAT(AllBitwiseCombinations(), IsEmpty()); +} + +TEST(AllBitwiseCombinationsTest, EmptyList) { + EXPECT_THAT(AllBitwiseCombinations(List{}), IsEmpty()); +} + +TEST(AllBitwiseCombinationsTest, SingleElementList) { + EXPECT_THAT(AllBitwiseCombinations(List{5}), UnorderedElementsAre(5)); +} + +TEST(AllBitwiseCombinationsTest, SingleList) { + EXPECT_THAT(AllBitwiseCombinations(List{0, 1, 2, 4}), + UnorderedElementsAre(0, 1, 2, 4)); +} + +TEST(AllBitwiseCombinationsTest, MultipleLists) { + EXPECT_THAT( + AllBitwiseCombinations(List{0, 1, 2, 3}, List{0, 4, 8, 12}), + UnorderedElementsAreArray( + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})); +} + +TEST(RandomizeBuffer, Works) { + const std::vector original(4096); + std::vector buffer = original; + RandomizeBuffer(buffer.data(), buffer.size()); + EXPECT_NE(buffer, original); +} + +// Enable comparison of vectors of iovec arrays for the following test. +MATCHER_P(IovecsListEq, expected, "") { + if (arg.size() != expected.size()) { + *result_listener << "sizes are different (actual: " << arg.size() + << ", expected: " << expected.size() << ")"; + return false; + } + + for (uint64_t i = 0; i < expected.size(); ++i) { + const std::vector& actual_iovecs = arg[i]; + const std::vector& expected_iovecs = expected[i]; + if (actual_iovecs.size() != expected_iovecs.size()) { + *result_listener << "iovec array size at position " << i + << " is different (actual: " << actual_iovecs.size() + << ", expected: " << expected_iovecs.size() << ")"; + return false; + } + + for (uint64_t j = 0; j < expected_iovecs.size(); ++j) { + const struct iovec& actual_iov = actual_iovecs[j]; + const struct iovec& expected_iov = expected_iovecs[j]; + if (actual_iov.iov_base != expected_iov.iov_base) { + *result_listener << "iovecs in array " << i << " at position " << j + << " are different (expected iov_base: " + << expected_iov.iov_base + << ", got: " << actual_iov.iov_base << ")"; + return false; + } + if (actual_iov.iov_len != expected_iov.iov_len) { + *result_listener << "iovecs in array " << i << " at position " << j + << " are different (expected iov_len: " + << expected_iov.iov_len + << ", got: " << actual_iov.iov_len << ")"; + return false; + } + } + } + + return true; +} + +// Verify empty iovec list generation. +TEST(GenerateIovecs, EmptyList) { + std::vector buffer = {'a', 'b', 'c'}; + + EXPECT_THAT(GenerateIovecs(0, buffer.data(), buffer.size()), + IovecsListEq(std::vector>())); +} + +// Verify generating a single array of only one, partial, iovec. +TEST(GenerateIovecs, OneArray) { + std::vector buffer = {'a', 'b', 'c'}; + + std::vector> expected; + struct iovec iov = {}; + iov.iov_base = buffer.data(); + iov.iov_len = 2; + expected.push_back(std::vector({iov})); + EXPECT_THAT(GenerateIovecs(2, buffer.data(), buffer.size()), + IovecsListEq(expected)); +} + +// Verify that it wraps around after IOV_MAX iovecs. +TEST(GenerateIovecs, WrapsAtIovMax) { + std::vector buffer = {'a', 'b', 'c'}; + + std::vector> expected; + struct iovec iov = {}; + iov.iov_base = buffer.data(); + iov.iov_len = buffer.size(); + expected.emplace_back(); + for (int i = 0; i < IOV_MAX; ++i) { + expected[0].push_back(iov); + } + iov.iov_len = 1; + expected.push_back(std::vector({iov})); + + EXPECT_THAT( + GenerateIovecs(IOV_MAX * buffer.size() + 1, buffer.data(), buffer.size()), + IovecsListEq(expected)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/util/thread_util.h b/test/util/thread_util.h new file mode 100644 index 000000000..df09ac8cf --- /dev/null +++ b/test/util/thread_util.h @@ -0,0 +1,89 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_THREAD_UTIL_H_ +#define GVISOR_TEST_UTIL_THREAD_UTIL_H_ + +#include +#include +#include + +#include +#include + +#include "test/util/logging.h" + +namespace gvisor { +namespace testing { + +// ScopedThread is a minimal wrapper around pthreads. +// +// This is used in lieu of more complex mechanisms because it provides very +// predictable behavior (no messing with timers, etc.) The thread will +// automatically joined when it is destructed (goes out of scope), but can be +// joined manually as well. +class ScopedThread { + public: + // Constructs a thread that executes f exactly once. + explicit ScopedThread(std::function f) : f_(std::move(f)) { + CreateThread(); + } + + explicit ScopedThread(const std::function& f) { + f_ = [=] { + f(); + return nullptr; + }; + CreateThread(); + } + + ScopedThread(const ScopedThread& other) = delete; + ScopedThread& operator=(const ScopedThread& other) = delete; + + // Joins the thread. + ~ScopedThread() { Join(); } + + // Waits until this thread has finished executing. Join is idempotent and may + // be called multiple times, however Join itself is not thread-safe. + void* Join() { + if (!joined_) { + TEST_PCHECK(pthread_join(pt_, &retval_) == 0); + joined_ = true; + } + return retval_; + } + + private: + void CreateThread() { + TEST_PCHECK_MSG( + pthread_create(&pt_, /* attr = */ nullptr, + +[](void* arg) -> void* { + return static_cast(arg)->f_(); + }, + this) == 0, + "thread creation failed"); + } + + std::function f_; + pthread_t pt_; + bool joined_ = false; + void* retval_ = nullptr; +}; + +inline pid_t gettid() { return syscall(SYS_gettid); } + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_THREAD_UTIL_H_ diff --git a/test/util/timer_util.cc b/test/util/timer_util.cc new file mode 100644 index 000000000..681fafb69 --- /dev/null +++ b/test/util/timer_util.cc @@ -0,0 +1,27 @@ +// Copyright 2018 Google LLC +// +// 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. + +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +absl::Time Now(clockid_t id) { + struct timespec now; + TEST_PCHECK(clock_gettime(id, &now) == 0); + return absl::TimeFromTimespec(now); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/timer_util.h b/test/util/timer_util.h new file mode 100644 index 000000000..9bdc51a57 --- /dev/null +++ b/test/util/timer_util.h @@ -0,0 +1,74 @@ +// Copyright 2018 Google LLC +// +// 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. + +#ifndef GVISOR_TEST_UTIL_TIMER_UTIL_H_ +#define GVISOR_TEST_UTIL_TIMER_UTIL_H_ + +#include +#include + +#include + +#include "gmock/gmock.h" +#include "absl/time/time.h" +#include "test/util/cleanup.h" +#include "test/util/logging.h" +#include "test/util/posix_error.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// MonotonicTimer is a simple timer that uses a monotic clock. +class MonotonicTimer { + public: + MonotonicTimer() {} + absl::Duration Duration() { + struct timespec ts; + TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + return absl::TimeFromTimespec(ts) - start_; + } + + void Start() { + struct timespec ts; + TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + start_ = absl::TimeFromTimespec(ts); + } + + protected: + absl::Time start_; +}; + +// Sets the given itimer and returns a cleanup function that restores the +// previous itimer when it goes out of scope. +inline PosixErrorOr ScopedItimer(int which, + struct itimerval const& new_value) { + struct itimerval old_value; + int rc = setitimer(which, &new_value, &old_value); + MaybeSave(); + if (rc < 0) { + return PosixError(errno, "setitimer failed"); + } + return Cleanup(std::function([which, old_value] { + EXPECT_THAT(setitimer(which, &old_value, nullptr), SyscallSucceeds()); + })); +} + +// Returns the current time. +absl::Time Now(clockid_t id); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_TIMER_UTIL_H_ -- cgit v1.2.3 From 0d7023d581612e1670ef36490a827e46968d6d08 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Wed, 9 Jan 2019 09:17:04 -0800 Subject: Restore to original cgroup after sandbox and gofer processes are created The original code assumed that it was safe to join and not restore cgroup, but Container.Run will not exit after calling start, making cgroup cleanup fail because there were still processes inside the cgroup. PiperOrigin-RevId: 228529199 Change-Id: I12a48d9adab4bbb02f20d71ec99598c336cbfe51 --- runsc/cgroup/cgroup.go | 65 +++++++++++++++++++++++++++++++++++--------- runsc/container/BUILD | 1 + runsc/container/container.go | 62 +++++++++++++++++++++++++++++------------- runsc/sandbox/sandbox.go | 37 ++++--------------------- runsc/specutils/specutils.go | 8 +++--- 5 files changed, 106 insertions(+), 67 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go index 2887f3d7f..65a0b6d7a 100644 --- a/runsc/cgroup/cgroup.go +++ b/runsc/cgroup/cgroup.go @@ -17,6 +17,7 @@ package cgroup import ( + "bufio" "context" "fmt" "io/ioutil" @@ -168,12 +169,12 @@ type Cgroup struct { } // New creates a new Cgroup instance if the spec includes a cgroup path. -// Otherwise it returns nil and false. -func New(spec *specs.Spec) (*Cgroup, bool) { +// Returns nil otherwise. +func New(spec *specs.Spec) *Cgroup { if spec.Linux == nil || spec.Linux.CgroupsPath == "" { - return nil, false + return nil } - return &Cgroup{Name: spec.Linux.CgroupsPath}, true + return &Cgroup{Name: spec.Linux.CgroupsPath} } // Install creates and configures cgroups according to 'res'. If cgroup path @@ -241,19 +242,57 @@ func (c *Cgroup) Uninstall() error { return nil } -// Join adds the current process to the all controllers. -func (c *Cgroup) Join() error { - return c.Add(0) -} +// Join adds the current process to the all controllers. Returns function that +// restores cgroup to the original state. +func (c *Cgroup) Join() (func(), error) { + // First save the current state so it can be restored. + undo := func() {} + f, err := os.Open("/proc/self/cgroup") + if err != nil { + return undo, err + } + defer f.Close() + + var undoPaths []string + scanner := bufio.NewScanner(f) + for scanner.Scan() { + // Format: ID:controller1,controller2:path + // Example: 2:cpu,cpuacct:/user.slice + tokens := strings.Split(scanner.Text(), ":") + if len(tokens) != 3 { + return undo, fmt.Errorf("formatting cgroups file, line: %q", scanner.Text()) + } + for _, ctrlr := range strings.Split(tokens[1], ",") { + // Skip controllers we don't handle. + if _, ok := controllers[ctrlr]; ok { + undoPaths = append(undoPaths, filepath.Join(cgroupRoot, ctrlr, tokens[2])) + break + } + } + } + if err := scanner.Err(); err != nil { + return undo, err + } -// Add adds given process to all controllers. -func (c *Cgroup) Add(pid int) error { + // Replace empty undo with the real thing before changes are made to cgroups. + undo = func() { + for _, path := range undoPaths { + log.Debugf("Restoring cgroup %q", path) + if err := setValue(path, "cgroup.procs", "0"); err != nil { + log.Warningf("Error restoring cgroup %q: %v", path, err) + } + } + } + + // Now join the cgroups. for key := range controllers { - if err := setValue(c.makePath(key), "cgroup.procs", strconv.Itoa(pid)); err != nil { - return err + path := c.makePath(key) + log.Debugf("Joining cgroup %q", path) + if err := setValue(path, "cgroup.procs", "0"); err != nil { + return undo, err } } - return nil + return undo, nil } // NumCPU returns the number of CPUs configured in 'cpuset/cpuset.cpus'. diff --git a/runsc/container/BUILD b/runsc/container/BUILD index 28ec81d3f..d9534cbcc 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -19,6 +19,7 @@ go_library( "//pkg/log", "//pkg/sentry/control", "//runsc/boot", + "//runsc/cgroup", "//runsc/sandbox", "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", diff --git a/runsc/container/container.go b/runsc/container/container.go index 07924d23a..dc9ef86fa 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -36,6 +36,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/log" "gvisor.googlesource.com/gvisor/pkg/sentry/control" "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/cgroup" "gvisor.googlesource.com/gvisor/runsc/sandbox" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -286,18 +287,26 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo return nil, fmt.Errorf("writing clean spec: %v", err) } - ioFiles, err := c.createGoferProcess(spec, conf, bundleDir) - if err != nil { - return nil, err + // Create and join cgroup before processes are created to ensure they are + // part of the cgroup from the start (and all tneir children processes). + cg := cgroup.New(spec) + if cg != nil { + // If there is cgroup config, install it before creating sandbox process. + if err := cg.Install(spec.Linux.Resources); err != nil { + return nil, fmt.Errorf("configuring cgroup: %v", err) + } } + if err := runInCgroup(cg, func() error { + ioFiles, err := c.createGoferProcess(spec, conf, bundleDir) + if err != nil { + return err + } - // Start a new sandbox for this container. Any errors after this point - // must destroy the container. - c.Sandbox, err = sandbox.Create(id, spec, conf, bundleDir, consoleSocket, userLog, ioFiles) - if err != nil { - return nil, err - } - if err := c.Sandbox.AddGoferToCgroup(c.GoferPid); err != nil { + // Start a new sandbox for this container. Any errors after this point + // must destroy the container. + c.Sandbox, err = sandbox.New(id, spec, conf, bundleDir, consoleSocket, userLog, ioFiles, cg) + return err + }); err != nil { return nil, err } } else { @@ -381,15 +390,16 @@ func (c *Container) Start(conf *boot.Config) error { return fmt.Errorf("writing clean spec: %v", err) } - // Create the gofer process. - ioFiles, err := c.createGoferProcess(c.Spec, conf, c.BundleDir) - if err != nil { - return err - } - if err := c.Sandbox.StartContainer(c.Spec, conf, c.ID, ioFiles); err != nil { - return err - } - if err := c.Sandbox.AddGoferToCgroup(c.GoferPid); err != nil { + // Join cgroup to strt gofer process to ensure it's part of the cgroup from + // the start (and all tneir children processes). + if err := runInCgroup(c.Sandbox.Cgroup, func() error { + // Create the gofer process. + ioFiles, err := c.createGoferProcess(c.Spec, conf, c.BundleDir) + if err != nil { + return err + } + return c.Sandbox.StartContainer(c.Spec, conf, c.ID, ioFiles) + }); err != nil { return err } } @@ -883,3 +893,17 @@ func lockContainerMetadata(containerRootDir string) (func() error, error) { } return l.Unlock, nil } + +// runInCgroup executes fn inside the specified cgroup. If cg is nil, execute +// it in the current context. +func runInCgroup(cg *cgroup.Cgroup, fn func() error) error { + if cg == nil { + return fn() + } + restore, err := cg.Join() + defer restore() + if err != nil { + return err + } + return fn() +} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index d84995d04..9e95a11b4 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -64,32 +64,15 @@ type Sandbox struct { Cgroup *cgroup.Cgroup `json:"cgroup"` } -// Create creates the sandbox process. The caller must call Destroy() on the -// sandbox. If spec specified a cgroup, the current process will have joined -// the cgroup upon return. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File) (*Sandbox, error) { - s := &Sandbox{ID: id} - // The Cleanup object cleans up partially created sandboxes when an error occurs. - // Any errors occurring during cleanup itself are ignored. +// New creates the sandbox process. The caller must call Destroy() on the +// sandbox. +func New(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File, cg *cgroup.Cgroup) (*Sandbox, error) { + s := &Sandbox{ID: id, Cgroup: cg} + // The Cleanup object cleans up partially created sandboxes when an error + // occurs. Any errors occurring during cleanup itself are ignored. c := specutils.MakeCleanup(func() { _ = s.destroy() }) defer c.Clean() - if cg, ok := cgroup.New(spec); ok { - s.Cgroup = cg - - // If there is cgroup config, install it before creating sandbox process. - if err := s.Cgroup.Install(spec.Linux.Resources); err != nil { - return nil, fmt.Errorf("configuring cgroup: %v", err) - } - - // Make this process join the cgroup to ensure the sandbox (and all its - // children processes) are part of the cgroup from the start. Don't bother - // moving out because the caller is about to exit anyways. - if err := cg.Join(); err != nil { - return nil, fmt.Errorf("joining cgroup: %v", err) - } - } - // Create pipe to synchronize when sandbox process has been booted. fds := make([]int, 2) if err := syscall.Pipe(fds); err != nil { @@ -871,14 +854,6 @@ func (s *Sandbox) waitForStopped() error { return backoff.Retry(op, b) } -// AddGoferToCgroup adds the gofer process to the sandbox's cgroup. -func (s *Sandbox) AddGoferToCgroup(pid int) error { - if s.Cgroup != nil { - return s.Cgroup.Add(pid) - } - return nil -} - // deviceFileForPlatform opens the device file for the given platform. If the // platform does not need a device file, then nil is returned. func deviceFileForPlatform(p boot.PlatformType) (*os.File, error) { diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 055076475..7b0dcf231 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -470,8 +470,7 @@ func ContainsStr(strs []string, str string) bool { // c.Release() // on success, aborts closing the file and return it. // return f type Cleanup struct { - clean func() - released bool + clean func() } // MakeCleanup creates a new Cleanup object. @@ -481,13 +480,14 @@ func MakeCleanup(f func()) Cleanup { // Clean calls the cleanup function. func (c *Cleanup) Clean() { - if !c.released { + if c.clean != nil { c.clean() + c.clean = nil } } // Release releases the cleanup from its duties, i.e. cleanup function is not // called after this point. func (c *Cleanup) Release() { - c.released = true + c.clean = nil } -- cgit v1.2.3 From 5f08f8fd8162fa2fc2ca7b862263081d8d07b206 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 22 Jan 2019 16:45:45 -0800 Subject: Don't bind-mount runsc into a sandbox mntns PiperOrigin-RevId: 230437407 Change-Id: Id9d8ceeb018aad2fe317407c78c6ee0f4b47aa2b --- runsc/cmd/boot.go | 1 - runsc/cmd/chroot.go | 8 -------- runsc/cmd/cmd.go | 9 +++------ runsc/cmd/exec.go | 6 ++---- runsc/container/container.go | 6 ++---- runsc/sandbox/sandbox.go | 5 +---- runsc/specutils/specutils.go | 10 ---------- runsc/test/root/chroot_test.go | 13 ++++--------- 8 files changed, 12 insertions(+), 46 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 7f87b2623..3039b389f 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -129,7 +129,6 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) Fatalf("error setting up chroot: %v", err) } - specutils.ExePath = "/runsc" if !b.applyCaps { // Remove --setup-root arg to call myself. var args []string diff --git a/runsc/cmd/chroot.go b/runsc/cmd/chroot.go index ec539a11c..c1acbf26b 100644 --- a/runsc/cmd/chroot.go +++ b/runsc/cmd/chroot.go @@ -24,10 +24,6 @@ import ( "gvisor.googlesource.com/gvisor/runsc/specutils" ) -// chrootBinPath is the location inside the chroot where the runsc binary will -// be mounted. -const chrootBinPath = "/runsc" - // mountInChroot creates the destination mount point in the given chroot and // mounts the source. func mountInChroot(chroot, src, dst, typ string, flags uint32) error { @@ -70,10 +66,6 @@ func setUpChroot(pidns bool) error { } } - if err := mountInChroot(chroot, specutils.ExePath, chrootBinPath, "bind", syscall.MS_BIND|syscall.MS_RDONLY); err != nil { - return fmt.Errorf("error mounting runsc in chroot: %v", err) - } - if err := os.Chdir(chroot); err != nil { return fmt.Errorf("error changing working directory: %v", err) } diff --git a/runsc/cmd/cmd.go b/runsc/cmd/cmd.go index fbfc18fc9..208cf5304 100644 --- a/runsc/cmd/cmd.go +++ b/runsc/cmd/cmd.go @@ -80,13 +80,10 @@ func setCapsAndCallSelf(args []string, caps *specs.LinuxCapabilities) error { if err := applyCaps(caps); err != nil { return fmt.Errorf("applyCaps() failed: %v", err) } - binPath, err := specutils.BinPath() - if err != nil { - return err - } + binPath := specutils.ExePath log.Infof("Execve %q again, bye!", binPath) - err = syscall.Exec(binPath, args, []string{}) + err := syscall.Exec(binPath, args, []string{}) return fmt.Errorf("error executing %s: %v", binPath, err) } @@ -105,7 +102,7 @@ func callSelfAsNobody(args []string) error { return fmt.Errorf("error setting gid: %v", err) } - binPath := "/runsc" + binPath := specutils.ExePath log.Infof("Execve %q again, bye!", binPath) err := syscall.Exec(binPath, args, []string{}) diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index 13584d800..9e058ad97 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -186,10 +186,7 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } func (ex *Exec) execAndWait(waitStatus *syscall.WaitStatus) subcommands.ExitStatus { - binPath, err := specutils.BinPath() - if err != nil { - Fatalf("getting bin path: %v", err) - } + binPath := specutils.ExePath var args []string // The command needs to write a pid file so that execAndWait can tell @@ -219,6 +216,7 @@ func (ex *Exec) execAndWait(waitStatus *syscall.WaitStatus) subcommands.ExitStat } cmd := exec.Command(binPath, args...) + cmd.Args[0] = "runsc-exec" // Exec stdio defaults to current process stdio. cmd.Stdin = os.Stdin diff --git a/runsc/container/container.go b/runsc/container/container.go index 2d4b85d9f..6d88dff7f 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -818,12 +818,10 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bund args = append(args, fmt.Sprintf("--io-fds=%d", nextFD)) } - binPath, err := specutils.BinPath() - if err != nil { - return nil, err - } + binPath := specutils.ExePath cmd := exec.Command(binPath, args...) cmd.ExtraFiles = goferEnds + cmd.Args[0] = "runsc-gofer" // Enter new namespaces to isolate from the rest of the system. Don't unshare // cgroup because gofer is added to a cgroup in the caller's namespace. diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 53cb464d2..721a49141 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -292,10 +292,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 - binPath, err := specutils.BinPath() - if err != nil { - return err - } + binPath := specutils.ExePath cmd := exec.Command(binPath, conf.ToFlags()...) cmd.SysProcAttr = &syscall.SysProcAttr{} diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 7b0dcf231..4e7893ab4 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -315,16 +315,6 @@ func IsSupportedDevMount(m specs.Mount) bool { return true } -// BinPath returns the real path to self, resolving symbolink links. This is done -// to make the process name appears as 'runsc', instead of 'exe'. -func BinPath() (string, error) { - binPath, err := filepath.EvalSymlinks(ExePath) - if err != nil { - return "", fmt.Errorf(`error resolving %q symlink: %v`, ExePath, err) - } - return binPath, nil -} - const ( // ContainerdContainerTypeAnnotation is the OCI annotation set by // containerd to indicate whether the container to create should have diff --git a/runsc/test/root/chroot_test.go b/runsc/test/root/chroot_test.go index 04124703d..89f90c3e0 100644 --- a/runsc/test/root/chroot_test.go +++ b/runsc/test/root/chroot_test.go @@ -26,8 +26,6 @@ import ( "os" "os/exec" "path/filepath" - "reflect" - "sort" "strconv" "strings" "testing" @@ -73,16 +71,13 @@ func TestChroot(t *testing.T) { if err != nil { t.Fatalf("error listing %q: %v", chroot, err) } - if want, got := 2, len(fi); want != got { + if want, got := 1, len(fi); want != got { t.Fatalf("chroot dir got %d entries, want %d", got, want) } - // chroot dir is prepared by runsc and should contains only the executable - // and /proc. - files := []string{fi[0].Name(), fi[1].Name()} - sort.Strings(files) - if want := []string{"proc", "runsc"}; !reflect.DeepEqual(files, want) { - t.Errorf("chroot got children %v, want %v", files, want) + // chroot dir is prepared by runsc and should contains only /proc. + if fi[0].Name() != "proc" { + t.Errorf("chroot got children %v, want %v", fi[0].Name(), "proc") } d.CleanUp() -- cgit v1.2.3 From 2a0c69b19f4b55c3f9777f0098a72af123ccff3c Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Thu, 31 Jan 2019 11:11:44 -0800 Subject: Remove license comments Nothing reads them and they can simply get stale. Generated with: $ sed -i "s/licenses(\(.*\)).*/licenses(\1)/" **/BUILD PiperOrigin-RevId: 231818945 Change-Id: Ibc3f9838546b7e94f13f217060d31f4ada9d4bf0 --- pkg/abi/BUILD | 2 +- pkg/abi/linux/BUILD | 2 +- pkg/amutex/BUILD | 2 +- pkg/atomicbitops/BUILD | 2 +- pkg/binary/BUILD | 2 +- pkg/bits/BUILD | 2 +- pkg/bpf/BUILD | 2 +- pkg/compressio/BUILD | 2 +- pkg/control/client/BUILD | 2 +- pkg/control/server/BUILD | 2 +- pkg/cpuid/BUILD | 2 +- pkg/dhcp/BUILD | 2 +- pkg/eventchannel/BUILD | 2 +- pkg/fd/BUILD | 2 +- pkg/fdnotifier/BUILD | 2 +- pkg/gate/BUILD | 2 +- pkg/ilist/BUILD | 2 +- pkg/linewriter/BUILD | 2 +- pkg/log/BUILD | 2 +- pkg/metric/BUILD | 2 +- pkg/p9/BUILD | 2 +- pkg/p9/local_server/BUILD | 2 +- pkg/p9/p9test/BUILD | 2 +- pkg/rand/BUILD | 2 +- pkg/refs/BUILD | 2 +- pkg/seccomp/BUILD | 2 +- pkg/secio/BUILD | 2 +- pkg/segment/BUILD | 2 +- pkg/segment/test/BUILD | 2 +- pkg/sentry/BUILD | 2 +- pkg/sentry/arch/BUILD | 2 +- pkg/sentry/context/BUILD | 2 +- pkg/sentry/context/contexttest/BUILD | 2 +- pkg/sentry/control/BUILD | 2 +- pkg/sentry/device/BUILD | 2 +- pkg/sentry/fs/BUILD | 2 +- pkg/sentry/fs/anon/BUILD | 2 +- pkg/sentry/fs/ashmem/BUILD | 2 +- pkg/sentry/fs/binder/BUILD | 2 +- pkg/sentry/fs/dev/BUILD | 2 +- pkg/sentry/fs/fdpipe/BUILD | 2 +- pkg/sentry/fs/filetest/BUILD | 2 +- pkg/sentry/fs/fsutil/BUILD | 2 +- pkg/sentry/fs/gofer/BUILD | 2 +- pkg/sentry/fs/host/BUILD | 2 +- pkg/sentry/fs/lock/BUILD | 2 +- pkg/sentry/fs/proc/BUILD | 2 +- pkg/sentry/fs/proc/device/BUILD | 2 +- pkg/sentry/fs/proc/seqfile/BUILD | 2 +- pkg/sentry/fs/ramfs/BUILD | 2 +- pkg/sentry/fs/sys/BUILD | 2 +- pkg/sentry/fs/timerfd/BUILD | 2 +- pkg/sentry/fs/tmpfs/BUILD | 2 +- pkg/sentry/fs/tty/BUILD | 2 +- pkg/sentry/hostcpu/BUILD | 2 +- pkg/sentry/inet/BUILD | 2 +- pkg/sentry/kernel/BUILD | 2 +- pkg/sentry/kernel/auth/BUILD | 2 +- pkg/sentry/kernel/contexttest/BUILD | 2 +- pkg/sentry/kernel/epoll/BUILD | 2 +- pkg/sentry/kernel/eventfd/BUILD | 2 +- pkg/sentry/kernel/fasync/BUILD | 2 +- pkg/sentry/kernel/futex/BUILD | 2 +- pkg/sentry/kernel/kdefs/BUILD | 2 +- pkg/sentry/kernel/memevent/BUILD | 2 +- pkg/sentry/kernel/pipe/BUILD | 2 +- pkg/sentry/kernel/sched/BUILD | 2 +- pkg/sentry/kernel/semaphore/BUILD | 2 +- pkg/sentry/kernel/shm/BUILD | 2 +- pkg/sentry/kernel/time/BUILD | 2 +- pkg/sentry/limits/BUILD | 2 +- pkg/sentry/loader/BUILD | 2 +- pkg/sentry/memmap/BUILD | 2 +- pkg/sentry/memutil/BUILD | 2 +- pkg/sentry/mm/BUILD | 2 +- pkg/sentry/platform/BUILD | 2 +- pkg/sentry/platform/filemem/BUILD | 2 +- pkg/sentry/platform/interrupt/BUILD | 2 +- pkg/sentry/platform/kvm/BUILD | 2 +- pkg/sentry/platform/kvm/testutil/BUILD | 2 +- pkg/sentry/platform/procid/BUILD | 2 +- pkg/sentry/platform/ptrace/BUILD | 2 +- pkg/sentry/platform/ring0/BUILD | 2 +- pkg/sentry/platform/ring0/gen_offsets/BUILD | 2 +- pkg/sentry/platform/ring0/pagetables/BUILD | 2 +- pkg/sentry/platform/safecopy/BUILD | 2 +- pkg/sentry/safemem/BUILD | 2 +- pkg/sentry/sighandling/BUILD | 2 +- pkg/sentry/socket/BUILD | 2 +- pkg/sentry/socket/control/BUILD | 2 +- pkg/sentry/socket/epsocket/BUILD | 2 +- pkg/sentry/socket/hostinet/BUILD | 2 +- pkg/sentry/socket/netlink/BUILD | 2 +- pkg/sentry/socket/netlink/port/BUILD | 2 +- pkg/sentry/socket/netlink/route/BUILD | 2 +- pkg/sentry/socket/rpcinet/BUILD | 2 +- pkg/sentry/socket/rpcinet/conn/BUILD | 2 +- pkg/sentry/socket/rpcinet/notifier/BUILD | 2 +- pkg/sentry/socket/unix/BUILD | 2 +- pkg/sentry/socket/unix/transport/BUILD | 2 +- pkg/sentry/state/BUILD | 2 +- pkg/sentry/strace/BUILD | 2 +- pkg/sentry/syscalls/BUILD | 2 +- pkg/sentry/syscalls/linux/BUILD | 2 +- pkg/sentry/time/BUILD | 2 +- pkg/sentry/unimpl/BUILD | 2 +- pkg/sentry/uniqueid/BUILD | 2 +- pkg/sentry/usage/BUILD | 2 +- pkg/sentry/usermem/BUILD | 2 +- pkg/sentry/watchdog/BUILD | 2 +- pkg/sleep/BUILD | 2 +- pkg/state/BUILD | 2 +- pkg/state/statefile/BUILD | 2 +- pkg/sync/BUILD | 2 +- pkg/sync/atomicptrtest/BUILD | 2 +- pkg/sync/seqatomictest/BUILD | 2 +- pkg/syserr/BUILD | 2 +- pkg/syserror/BUILD | 2 +- pkg/tcpip/BUILD | 2 +- pkg/tcpip/adapters/gonet/BUILD | 2 +- pkg/tcpip/buffer/BUILD | 2 +- pkg/tcpip/checker/BUILD | 2 +- pkg/tcpip/hash/jenkins/BUILD | 2 +- pkg/tcpip/header/BUILD | 2 +- pkg/tcpip/link/channel/BUILD | 2 +- pkg/tcpip/link/fdbased/BUILD | 2 +- pkg/tcpip/link/loopback/BUILD | 2 +- pkg/tcpip/link/rawfile/BUILD | 2 +- pkg/tcpip/link/sharedmem/BUILD | 2 +- pkg/tcpip/link/sharedmem/pipe/BUILD | 2 +- pkg/tcpip/link/sharedmem/queue/BUILD | 2 +- pkg/tcpip/link/sniffer/BUILD | 2 +- pkg/tcpip/link/tun/BUILD | 2 +- pkg/tcpip/link/waitable/BUILD | 2 +- pkg/tcpip/network/BUILD | 2 +- pkg/tcpip/network/arp/BUILD | 2 +- pkg/tcpip/network/fragmentation/BUILD | 2 +- pkg/tcpip/network/hash/BUILD | 2 +- pkg/tcpip/network/ipv4/BUILD | 2 +- pkg/tcpip/network/ipv6/BUILD | 2 +- pkg/tcpip/ports/BUILD | 2 +- pkg/tcpip/sample/tun_tcp_connect/BUILD | 2 +- pkg/tcpip/sample/tun_tcp_echo/BUILD | 2 +- pkg/tcpip/seqnum/BUILD | 2 +- pkg/tcpip/stack/BUILD | 2 +- pkg/tcpip/transport/ping/BUILD | 2 +- pkg/tcpip/transport/tcp/BUILD | 2 +- pkg/tcpip/transport/tcp/testing/context/BUILD | 2 +- pkg/tcpip/transport/tcpconntrack/BUILD | 2 +- pkg/tcpip/transport/udp/BUILD | 2 +- pkg/tmutex/BUILD | 2 +- pkg/unet/BUILD | 2 +- pkg/urpc/BUILD | 2 +- pkg/waiter/BUILD | 2 +- runsc/boot/BUILD | 2 +- runsc/boot/filter/BUILD | 2 +- runsc/cgroup/BUILD | 2 +- runsc/cmd/BUILD | 2 +- runsc/console/BUILD | 2 +- runsc/container/BUILD | 2 +- runsc/fsgofer/BUILD | 2 +- runsc/fsgofer/filter/BUILD | 2 +- runsc/sandbox/BUILD | 2 +- runsc/specutils/BUILD | 2 +- runsc/test/image/BUILD | 2 +- runsc/test/integration/BUILD | 2 +- runsc/test/root/BUILD | 2 +- runsc/test/root/testdata/BUILD | 2 +- runsc/test/testutil/BUILD | 2 +- runsc/tools/dockercfg/BUILD | 2 +- test/syscalls/BUILD | 2 +- test/syscalls/gtest/BUILD | 2 +- test/syscalls/linux/BUILD | 2 +- test/util/BUILD | 2 +- tools/go_generics/BUILD | 2 +- tools/go_generics/globals/BUILD | 2 +- tools/go_generics/go_merge/BUILD | 2 +- tools/go_generics/rules_tests/BUILD | 2 +- tools/go_stateify/BUILD | 2 +- vdso/BUILD | 2 +- 180 files changed, 180 insertions(+), 180 deletions(-) (limited to 'runsc/specutils') diff --git a/pkg/abi/BUILD b/pkg/abi/BUILD index 1ba4f3a46..323263ebf 100644 --- a/pkg/abi/BUILD +++ b/pkg/abi/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/abi/linux/BUILD b/pkg/abi/linux/BUILD index e6043abf4..7648c9469 100644 --- a/pkg/abi/linux/BUILD +++ b/pkg/abi/linux/BUILD @@ -2,7 +2,7 @@ # Linux kernel. It should be used instead of syscall or golang.org/x/sys/unix # when the host OS may not be Linux. -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/amutex/BUILD b/pkg/amutex/BUILD index 7cda07418..bdb6e8f2c 100644 --- a/pkg/amutex/BUILD +++ b/pkg/amutex/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "amutex", diff --git a/pkg/atomicbitops/BUILD b/pkg/atomicbitops/BUILD index 235188531..9555bf645 100644 --- a/pkg/atomicbitops/BUILD +++ b/pkg/atomicbitops/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "atomicbitops", diff --git a/pkg/binary/BUILD b/pkg/binary/BUILD index 571151f72..bd37376b0 100644 --- a/pkg/binary/BUILD +++ b/pkg/binary/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "binary", diff --git a/pkg/bits/BUILD b/pkg/bits/BUILD index 46794bdb8..5214b2c24 100644 --- a/pkg/bits/BUILD +++ b/pkg/bits/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") diff --git a/pkg/bpf/BUILD b/pkg/bpf/BUILD index 564df3af5..3c7ae3103 100644 --- a/pkg/bpf/BUILD +++ b/pkg/bpf/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/compressio/BUILD b/pkg/compressio/BUILD index 72952d735..3a0ac64e6 100644 --- a/pkg/compressio/BUILD +++ b/pkg/compressio/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "compressio", diff --git a/pkg/control/client/BUILD b/pkg/control/client/BUILD index 32853875d..22a4a4a5a 100644 --- a/pkg/control/client/BUILD +++ b/pkg/control/client/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "client", diff --git a/pkg/control/server/BUILD b/pkg/control/server/BUILD index ba2b1be9f..76b2e9787 100644 --- a/pkg/control/server/BUILD +++ b/pkg/control/server/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "server", diff --git a/pkg/cpuid/BUILD b/pkg/cpuid/BUILD index 46fc4703b..29cc38778 100644 --- a/pkg/cpuid/BUILD +++ b/pkg/cpuid/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/dhcp/BUILD b/pkg/dhcp/BUILD index c97dfc14b..003620b48 100644 --- a/pkg/dhcp/BUILD +++ b/pkg/dhcp/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "dhcp", diff --git a/pkg/eventchannel/BUILD b/pkg/eventchannel/BUILD index 18348ef54..5c2a44aa1 100644 --- a/pkg/eventchannel/BUILD +++ b/pkg/eventchannel/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "eventchannel", diff --git a/pkg/fd/BUILD b/pkg/fd/BUILD index 06cfd445e..ab1109157 100644 --- a/pkg/fd/BUILD +++ b/pkg/fd/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "fd", diff --git a/pkg/fdnotifier/BUILD b/pkg/fdnotifier/BUILD index 27d378d5b..8c8d193cc 100644 --- a/pkg/fdnotifier/BUILD +++ b/pkg/fdnotifier/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "fdnotifier", diff --git a/pkg/gate/BUILD b/pkg/gate/BUILD index 9a87a3a31..83679f2da 100644 --- a/pkg/gate/BUILD +++ b/pkg/gate/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "gate", diff --git a/pkg/ilist/BUILD b/pkg/ilist/BUILD index a67aa2cff..dbd65ab12 100644 --- a/pkg/ilist/BUILD +++ b/pkg/ilist/BUILD @@ -1,7 +1,7 @@ load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "ilist", diff --git a/pkg/linewriter/BUILD b/pkg/linewriter/BUILD index 3f28ba867..d1aa2e7d6 100644 --- a/pkg/linewriter/BUILD +++ b/pkg/linewriter/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "linewriter", diff --git a/pkg/log/BUILD b/pkg/log/BUILD index 94ac66db3..b2d18eddb 100644 --- a/pkg/log/BUILD +++ b/pkg/log/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "log", diff --git a/pkg/metric/BUILD b/pkg/metric/BUILD index d96e5563b..4b2c7a00e 100644 --- a/pkg/metric/BUILD +++ b/pkg/metric/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "metric", diff --git a/pkg/p9/BUILD b/pkg/p9/BUILD index 2c224e65b..5d972309d 100644 --- a/pkg/p9/BUILD +++ b/pkg/p9/BUILD @@ -2,7 +2,7 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") package( default_visibility = ["//visibility:public"], - licenses = ["notice"], # Apache 2.0 + licenses = ["notice"], ) go_library( diff --git a/pkg/p9/local_server/BUILD b/pkg/p9/local_server/BUILD index b17ebb79d..aa6db186c 100644 --- a/pkg/p9/local_server/BUILD +++ b/pkg/p9/local_server/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "local_server", diff --git a/pkg/p9/p9test/BUILD b/pkg/p9/p9test/BUILD index 7c4b875ce..cf22edde8 100644 --- a/pkg/p9/p9test/BUILD +++ b/pkg/p9/p9test/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) alias( name = "mockgen", diff --git a/pkg/rand/BUILD b/pkg/rand/BUILD index 0c9efc709..4eec3a4dd 100644 --- a/pkg/rand/BUILD +++ b/pkg/rand/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "rand", diff --git a/pkg/refs/BUILD b/pkg/refs/BUILD index 98150ba8f..fc562f821 100644 --- a/pkg/refs/BUILD +++ b/pkg/refs/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/seccomp/BUILD b/pkg/seccomp/BUILD index 657f923ed..0e9c4692d 100644 --- a/pkg/seccomp/BUILD +++ b/pkg/seccomp/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_embed_data") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "victim", diff --git a/pkg/secio/BUILD b/pkg/secio/BUILD index 29f751725..2b4b87c61 100644 --- a/pkg/secio/BUILD +++ b/pkg/secio/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "secio", diff --git a/pkg/segment/BUILD b/pkg/segment/BUILD index 964d73af8..700385907 100644 --- a/pkg/segment/BUILD +++ b/pkg/segment/BUILD @@ -1,6 +1,6 @@ package( default_visibility = ["//:sandbox"], - licenses = ["notice"], # Apache 2.0 + licenses = ["notice"], ) load("//tools/go_generics:defs.bzl", "go_template") diff --git a/pkg/segment/test/BUILD b/pkg/segment/test/BUILD index bdf53e24e..81e929b8c 100644 --- a/pkg/segment/test/BUILD +++ b/pkg/segment/test/BUILD @@ -2,7 +2,7 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") package( default_visibility = ["//visibility:private"], - licenses = ["notice"], # Apache 2.0 + licenses = ["notice"], ) load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/sentry/BUILD b/pkg/sentry/BUILD index d18cf3555..53989301f 100644 --- a/pkg/sentry/BUILD +++ b/pkg/sentry/BUILD @@ -1,7 +1,7 @@ # This BUILD file defines a package_group that allows for interdependencies for # sentry-internal packages. -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) package_group( name = "internal", diff --git a/pkg/sentry/arch/BUILD b/pkg/sentry/arch/BUILD index 9bf04360a..0c044bc33 100644 --- a/pkg/sentry/arch/BUILD +++ b/pkg/sentry/arch/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/context/BUILD b/pkg/sentry/context/BUILD index 02d24defd..a3c8d0177 100644 --- a/pkg/sentry/context/BUILD +++ b/pkg/sentry/context/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "context", diff --git a/pkg/sentry/context/contexttest/BUILD b/pkg/sentry/context/contexttest/BUILD index 01bb40b04..bed156b70 100644 --- a/pkg/sentry/context/contexttest/BUILD +++ b/pkg/sentry/context/contexttest/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/control/BUILD b/pkg/sentry/control/BUILD index c3b682d6f..f54e01ee8 100644 --- a/pkg/sentry/control/BUILD +++ b/pkg/sentry/control/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "control", diff --git a/pkg/sentry/device/BUILD b/pkg/sentry/device/BUILD index bebdb2939..01de708d3 100644 --- a/pkg/sentry/device/BUILD +++ b/pkg/sentry/device/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "device", diff --git a/pkg/sentry/fs/BUILD b/pkg/sentry/fs/BUILD index 6f368b0da..e58333da3 100644 --- a/pkg/sentry/fs/BUILD +++ b/pkg/sentry/fs/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/anon/BUILD b/pkg/sentry/fs/anon/BUILD index 4bd912e95..2111df2e8 100644 --- a/pkg/sentry/fs/anon/BUILD +++ b/pkg/sentry/fs/anon/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "anon", diff --git a/pkg/sentry/fs/ashmem/BUILD b/pkg/sentry/fs/ashmem/BUILD index e5bb661b5..dcf620dca 100644 --- a/pkg/sentry/fs/ashmem/BUILD +++ b/pkg/sentry/fs/ashmem/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/sentry/fs/binder/BUILD b/pkg/sentry/fs/binder/BUILD index 27155819e..8a448175f 100644 --- a/pkg/sentry/fs/binder/BUILD +++ b/pkg/sentry/fs/binder/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/fs/dev/BUILD b/pkg/sentry/fs/dev/BUILD index 85371032a..e5b962c8c 100644 --- a/pkg/sentry/fs/dev/BUILD +++ b/pkg/sentry/fs/dev/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/fs/fdpipe/BUILD b/pkg/sentry/fs/fdpipe/BUILD index 8a0937cda..098463e97 100644 --- a/pkg/sentry/fs/fdpipe/BUILD +++ b/pkg/sentry/fs/fdpipe/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/filetest/BUILD b/pkg/sentry/fs/filetest/BUILD index d137fee4c..05ca72aa0 100644 --- a/pkg/sentry/fs/filetest/BUILD +++ b/pkg/sentry/fs/filetest/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/fs/fsutil/BUILD b/pkg/sentry/fs/fsutil/BUILD index d4767642b..7dff970ea 100644 --- a/pkg/sentry/fs/fsutil/BUILD +++ b/pkg/sentry/fs/fsutil/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/gofer/BUILD b/pkg/sentry/fs/gofer/BUILD index 35ffadd13..f2c79b475 100644 --- a/pkg/sentry/fs/gofer/BUILD +++ b/pkg/sentry/fs/gofer/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/host/BUILD b/pkg/sentry/fs/host/BUILD index 6877eb161..ea2ca11bf 100644 --- a/pkg/sentry/fs/host/BUILD +++ b/pkg/sentry/fs/host/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/lock/BUILD b/pkg/sentry/fs/lock/BUILD index 3159ff1da..7164744b8 100644 --- a/pkg/sentry/fs/lock/BUILD +++ b/pkg/sentry/fs/lock/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/proc/BUILD b/pkg/sentry/fs/proc/BUILD index 74954f213..f6bc90634 100644 --- a/pkg/sentry/fs/proc/BUILD +++ b/pkg/sentry/fs/proc/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/proc/device/BUILD b/pkg/sentry/fs/proc/device/BUILD index ff7dacf07..64b0c5a3a 100644 --- a/pkg/sentry/fs/proc/device/BUILD +++ b/pkg/sentry/fs/proc/device/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "device", diff --git a/pkg/sentry/fs/proc/seqfile/BUILD b/pkg/sentry/fs/proc/seqfile/BUILD index b4ba64e10..6b44c0075 100644 --- a/pkg/sentry/fs/proc/seqfile/BUILD +++ b/pkg/sentry/fs/proc/seqfile/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/ramfs/BUILD b/pkg/sentry/fs/ramfs/BUILD index 4a629e38e..f36e4a5e8 100644 --- a/pkg/sentry/fs/ramfs/BUILD +++ b/pkg/sentry/fs/ramfs/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/sys/BUILD b/pkg/sentry/fs/sys/BUILD index 7de928e16..42e98230e 100644 --- a/pkg/sentry/fs/sys/BUILD +++ b/pkg/sentry/fs/sys/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/fs/timerfd/BUILD b/pkg/sentry/fs/timerfd/BUILD index ffdd7e0dc..0e06a5028 100644 --- a/pkg/sentry/fs/timerfd/BUILD +++ b/pkg/sentry/fs/timerfd/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/fs/tmpfs/BUILD b/pkg/sentry/fs/tmpfs/BUILD index c5ec85460..bf5b68869 100644 --- a/pkg/sentry/fs/tmpfs/BUILD +++ b/pkg/sentry/fs/tmpfs/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/fs/tty/BUILD b/pkg/sentry/fs/tty/BUILD index 011cb6955..bee2db3f3 100644 --- a/pkg/sentry/fs/tty/BUILD +++ b/pkg/sentry/fs/tty/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/hostcpu/BUILD b/pkg/sentry/hostcpu/BUILD index 33197cf14..b5067ae6d 100644 --- a/pkg/sentry/hostcpu/BUILD +++ b/pkg/sentry/hostcpu/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "hostcpu", diff --git a/pkg/sentry/inet/BUILD b/pkg/sentry/inet/BUILD index 159c50efb..e288d34e9 100644 --- a/pkg/sentry/inet/BUILD +++ b/pkg/sentry/inet/BUILD @@ -1,6 +1,6 @@ package( default_visibility = ["//:sandbox"], - licenses = ["notice"], # Apache 2.0 + licenses = ["notice"], ) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/kernel/BUILD b/pkg/sentry/kernel/BUILD index 7d41626dc..b230aff98 100644 --- a/pkg/sentry/kernel/BUILD +++ b/pkg/sentry/kernel/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/kernel/auth/BUILD b/pkg/sentry/kernel/auth/BUILD index a81085372..abd4f2dae 100644 --- a/pkg/sentry/kernel/auth/BUILD +++ b/pkg/sentry/kernel/auth/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/kernel/contexttest/BUILD b/pkg/sentry/kernel/contexttest/BUILD index 391986291..5769a3b28 100644 --- a/pkg/sentry/kernel/contexttest/BUILD +++ b/pkg/sentry/kernel/contexttest/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/kernel/epoll/BUILD b/pkg/sentry/kernel/epoll/BUILD index 5e8b36ed6..1567d5050 100644 --- a/pkg/sentry/kernel/epoll/BUILD +++ b/pkg/sentry/kernel/epoll/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/kernel/eventfd/BUILD b/pkg/sentry/kernel/eventfd/BUILD index d96803fc9..f2f1a1223 100644 --- a/pkg/sentry/kernel/eventfd/BUILD +++ b/pkg/sentry/kernel/eventfd/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/kernel/fasync/BUILD b/pkg/sentry/kernel/fasync/BUILD index 17749c0de..5faf95909 100644 --- a/pkg/sentry/kernel/fasync/BUILD +++ b/pkg/sentry/kernel/fasync/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/kernel/futex/BUILD b/pkg/sentry/kernel/futex/BUILD index afd35985f..da24c36c1 100644 --- a/pkg/sentry/kernel/futex/BUILD +++ b/pkg/sentry/kernel/futex/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/kernel/kdefs/BUILD b/pkg/sentry/kernel/kdefs/BUILD index 3f8fa206c..38aaca134 100644 --- a/pkg/sentry/kernel/kdefs/BUILD +++ b/pkg/sentry/kernel/kdefs/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "kdefs", diff --git a/pkg/sentry/kernel/memevent/BUILD b/pkg/sentry/kernel/memevent/BUILD index dfd8dd062..347a69062 100644 --- a/pkg/sentry/kernel/memevent/BUILD +++ b/pkg/sentry/kernel/memevent/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "memevent", diff --git a/pkg/sentry/kernel/pipe/BUILD b/pkg/sentry/kernel/pipe/BUILD index 19b23c6d2..011a3f349 100644 --- a/pkg/sentry/kernel/pipe/BUILD +++ b/pkg/sentry/kernel/pipe/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/kernel/sched/BUILD b/pkg/sentry/kernel/sched/BUILD index 52e226a39..184e8a35b 100644 --- a/pkg/sentry/kernel/sched/BUILD +++ b/pkg/sentry/kernel/sched/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "sched", diff --git a/pkg/sentry/kernel/semaphore/BUILD b/pkg/sentry/kernel/semaphore/BUILD index bdcf4ce5c..840943ca8 100644 --- a/pkg/sentry/kernel/semaphore/BUILD +++ b/pkg/sentry/kernel/semaphore/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/kernel/shm/BUILD b/pkg/sentry/kernel/shm/BUILD index 40e641355..f45770eef 100644 --- a/pkg/sentry/kernel/shm/BUILD +++ b/pkg/sentry/kernel/shm/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/kernel/time/BUILD b/pkg/sentry/kernel/time/BUILD index 5d8db2273..584f7c7cc 100644 --- a/pkg/sentry/kernel/time/BUILD +++ b/pkg/sentry/kernel/time/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/limits/BUILD b/pkg/sentry/limits/BUILD index 90f4395d4..800166675 100644 --- a/pkg/sentry/limits/BUILD +++ b/pkg/sentry/limits/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/loader/BUILD b/pkg/sentry/loader/BUILD index 24e734b49..1ea260a4e 100644 --- a/pkg/sentry/loader/BUILD +++ b/pkg/sentry/loader/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_embed_data") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/memmap/BUILD b/pkg/sentry/memmap/BUILD index c9e0b95a0..9c2cbd18b 100644 --- a/pkg/sentry/memmap/BUILD +++ b/pkg/sentry/memmap/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/memutil/BUILD b/pkg/sentry/memutil/BUILD index 88738d65d..68b03d4cc 100644 --- a/pkg/sentry/memutil/BUILD +++ b/pkg/sentry/memutil/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "memutil", diff --git a/pkg/sentry/mm/BUILD b/pkg/sentry/mm/BUILD index 0997ec0a7..f679262d0 100644 --- a/pkg/sentry/mm/BUILD +++ b/pkg/sentry/mm/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/platform/BUILD b/pkg/sentry/platform/BUILD index af9ba5394..ac8a6cb7f 100644 --- a/pkg/sentry/platform/BUILD +++ b/pkg/sentry/platform/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/platform/filemem/BUILD b/pkg/sentry/platform/filemem/BUILD index 2a5982763..1a61cfaa5 100644 --- a/pkg/sentry/platform/filemem/BUILD +++ b/pkg/sentry/platform/filemem/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/platform/interrupt/BUILD b/pkg/sentry/platform/interrupt/BUILD index dbafa3204..eeccd4d0e 100644 --- a/pkg/sentry/platform/interrupt/BUILD +++ b/pkg/sentry/platform/interrupt/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "interrupt", diff --git a/pkg/sentry/platform/kvm/BUILD b/pkg/sentry/platform/kvm/BUILD index 1b71e629f..6e40b3177 100644 --- a/pkg/sentry/platform/kvm/BUILD +++ b/pkg/sentry/platform/kvm/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/sentry/platform/kvm/testutil/BUILD b/pkg/sentry/platform/kvm/testutil/BUILD index 1dffe94a4..e10087e8e 100644 --- a/pkg/sentry/platform/kvm/testutil/BUILD +++ b/pkg/sentry/platform/kvm/testutil/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "testutil", diff --git a/pkg/sentry/platform/procid/BUILD b/pkg/sentry/platform/procid/BUILD index 20c8bc02c..277509624 100644 --- a/pkg/sentry/platform/procid/BUILD +++ b/pkg/sentry/platform/procid/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "procid", diff --git a/pkg/sentry/platform/ptrace/BUILD b/pkg/sentry/platform/ptrace/BUILD index 2eb354ad4..f86790942 100644 --- a/pkg/sentry/platform/ptrace/BUILD +++ b/pkg/sentry/platform/ptrace/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "ptrace", diff --git a/pkg/sentry/platform/ring0/BUILD b/pkg/sentry/platform/ring0/BUILD index c35d49f2d..ecb3e9a9c 100644 --- a/pkg/sentry/platform/ring0/BUILD +++ b/pkg/sentry/platform/ring0/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") diff --git a/pkg/sentry/platform/ring0/gen_offsets/BUILD b/pkg/sentry/platform/ring0/gen_offsets/BUILD index b76d7974e..d7029d5a9 100644 --- a/pkg/sentry/platform/ring0/gen_offsets/BUILD +++ b/pkg/sentry/platform/ring0/gen_offsets/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/sentry/platform/ring0/pagetables/BUILD b/pkg/sentry/platform/ring0/pagetables/BUILD index de1b920af..fe93d3030 100644 --- a/pkg/sentry/platform/ring0/pagetables/BUILD +++ b/pkg/sentry/platform/ring0/pagetables/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") diff --git a/pkg/sentry/platform/safecopy/BUILD b/pkg/sentry/platform/safecopy/BUILD index cb8347dd8..05a6a61ae 100644 --- a/pkg/sentry/platform/safecopy/BUILD +++ b/pkg/sentry/platform/safecopy/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0, portions BSD, MIT +package(licenses = ["notice"]) go_library( name = "safecopy", diff --git a/pkg/sentry/safemem/BUILD b/pkg/sentry/safemem/BUILD index 87a9bff12..3ab453718 100644 --- a/pkg/sentry/safemem/BUILD +++ b/pkg/sentry/safemem/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "safemem", diff --git a/pkg/sentry/sighandling/BUILD b/pkg/sentry/sighandling/BUILD index 41313d334..cec3af92e 100644 --- a/pkg/sentry/sighandling/BUILD +++ b/pkg/sentry/sighandling/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "sighandling", diff --git a/pkg/sentry/socket/BUILD b/pkg/sentry/socket/BUILD index 3a8044b5f..076f953e7 100644 --- a/pkg/sentry/socket/BUILD +++ b/pkg/sentry/socket/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/socket/control/BUILD b/pkg/sentry/socket/control/BUILD index d3a63f15f..9f4763906 100644 --- a/pkg/sentry/socket/control/BUILD +++ b/pkg/sentry/socket/control/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/socket/epsocket/BUILD b/pkg/sentry/socket/epsocket/BUILD index da4aaf510..45e418db3 100644 --- a/pkg/sentry/socket/epsocket/BUILD +++ b/pkg/sentry/socket/epsocket/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/socket/hostinet/BUILD b/pkg/sentry/socket/hostinet/BUILD index b8dceb102..a469af7ac 100644 --- a/pkg/sentry/socket/hostinet/BUILD +++ b/pkg/sentry/socket/hostinet/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/socket/netlink/BUILD b/pkg/sentry/socket/netlink/BUILD index cff922cb8..148306329 100644 --- a/pkg/sentry/socket/netlink/BUILD +++ b/pkg/sentry/socket/netlink/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/socket/netlink/port/BUILD b/pkg/sentry/socket/netlink/port/BUILD index 3a7dbc5ed..a7370a4ec 100644 --- a/pkg/sentry/socket/netlink/port/BUILD +++ b/pkg/sentry/socket/netlink/port/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/socket/netlink/route/BUILD b/pkg/sentry/socket/netlink/route/BUILD index e1bcfe252..be0419679 100644 --- a/pkg/sentry/socket/netlink/route/BUILD +++ b/pkg/sentry/socket/netlink/route/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/socket/rpcinet/BUILD b/pkg/sentry/socket/rpcinet/BUILD index 06e121946..4da14a1e0 100644 --- a/pkg/sentry/socket/rpcinet/BUILD +++ b/pkg/sentry/socket/rpcinet/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "rpcinet", diff --git a/pkg/sentry/socket/rpcinet/conn/BUILD b/pkg/sentry/socket/rpcinet/conn/BUILD index a16977f29..4336ae9b4 100644 --- a/pkg/sentry/socket/rpcinet/conn/BUILD +++ b/pkg/sentry/socket/rpcinet/conn/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # BSD +package(licenses = ["notice"]) go_library( name = "conn", diff --git a/pkg/sentry/socket/rpcinet/notifier/BUILD b/pkg/sentry/socket/rpcinet/notifier/BUILD index 2bab01774..b0b107ddb 100644 --- a/pkg/sentry/socket/rpcinet/notifier/BUILD +++ b/pkg/sentry/socket/rpcinet/notifier/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # BSD +package(licenses = ["notice"]) go_library( name = "notifier", diff --git a/pkg/sentry/socket/unix/BUILD b/pkg/sentry/socket/unix/BUILD index a12fa93db..fe6871cc6 100644 --- a/pkg/sentry/socket/unix/BUILD +++ b/pkg/sentry/socket/unix/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/socket/unix/transport/BUILD b/pkg/sentry/socket/unix/transport/BUILD index 5a90837bc..5a2de0c4c 100644 --- a/pkg/sentry/socket/unix/transport/BUILD +++ b/pkg/sentry/socket/unix/transport/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/sentry/state/BUILD b/pkg/sentry/state/BUILD index f1f6fdb7d..42c459acc 100644 --- a/pkg/sentry/state/BUILD +++ b/pkg/sentry/state/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "state", diff --git a/pkg/sentry/strace/BUILD b/pkg/sentry/strace/BUILD index 8517db1ac..552e79686 100644 --- a/pkg/sentry/strace/BUILD +++ b/pkg/sentry/strace/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "strace", diff --git a/pkg/sentry/syscalls/BUILD b/pkg/sentry/syscalls/BUILD index 35192ff49..6b5469e45 100644 --- a/pkg/sentry/syscalls/BUILD +++ b/pkg/sentry/syscalls/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "syscalls", diff --git a/pkg/sentry/syscalls/linux/BUILD b/pkg/sentry/syscalls/linux/BUILD index 7621bfdbd..846601881 100644 --- a/pkg/sentry/syscalls/linux/BUILD +++ b/pkg/sentry/syscalls/linux/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/time/BUILD b/pkg/sentry/time/BUILD index 1191010e6..c4b6dcc63 100644 --- a/pkg/sentry/time/BUILD +++ b/pkg/sentry/time/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0, portions BSD +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/sentry/unimpl/BUILD b/pkg/sentry/unimpl/BUILD index 42e24ace5..b608867a9 100644 --- a/pkg/sentry/unimpl/BUILD +++ b/pkg/sentry/unimpl/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) proto_library( name = "unimplemented_syscall_proto", diff --git a/pkg/sentry/uniqueid/BUILD b/pkg/sentry/uniqueid/BUILD index 0929497c3..ccc5a28d3 100644 --- a/pkg/sentry/uniqueid/BUILD +++ b/pkg/sentry/uniqueid/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "uniqueid", diff --git a/pkg/sentry/usage/BUILD b/pkg/sentry/usage/BUILD index 868dfd400..09198496b 100644 --- a/pkg/sentry/usage/BUILD +++ b/pkg/sentry/usage/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/sentry/usermem/BUILD b/pkg/sentry/usermem/BUILD index dae41ed0e..1a560b6f3 100644 --- a/pkg/sentry/usermem/BUILD +++ b/pkg/sentry/usermem/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/sentry/watchdog/BUILD b/pkg/sentry/watchdog/BUILD index b2c687b20..0bbf3705c 100644 --- a/pkg/sentry/watchdog/BUILD +++ b/pkg/sentry/watchdog/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "watchdog", diff --git a/pkg/sleep/BUILD b/pkg/sleep/BUILD index 338fd9336..2b005bf66 100644 --- a/pkg/sleep/BUILD +++ b/pkg/sleep/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "sleep", diff --git a/pkg/state/BUILD b/pkg/state/BUILD index dd0f250fa..0a975e162 100644 --- a/pkg/state/BUILD +++ b/pkg/state/BUILD @@ -1,7 +1,7 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/state/statefile/BUILD b/pkg/state/statefile/BUILD index 66c8f3807..5967781e8 100644 --- a/pkg/state/statefile/BUILD +++ b/pkg/state/statefile/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "statefile", diff --git a/pkg/sync/BUILD b/pkg/sync/BUILD index 6ddc6e812..1624e681c 100644 --- a/pkg/sync/BUILD +++ b/pkg/sync/BUILD @@ -2,7 +2,7 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") package( default_visibility = ["//:sandbox"], - licenses = ["notice"], # Apache 2.0, portions BSD + licenses = ["notice"], ) load("//tools/go_generics:defs.bzl", "go_template") diff --git a/pkg/sync/atomicptrtest/BUILD b/pkg/sync/atomicptrtest/BUILD index 9cb7f66fe..198fbb895 100644 --- a/pkg/sync/atomicptrtest/BUILD +++ b/pkg/sync/atomicptrtest/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/sync/seqatomictest/BUILD b/pkg/sync/seqatomictest/BUILD index 54f8e59b1..23132650a 100644 --- a/pkg/sync/seqatomictest/BUILD +++ b/pkg/sync/seqatomictest/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") diff --git a/pkg/syserr/BUILD b/pkg/syserr/BUILD index 30ae20772..0d65115ef 100644 --- a/pkg/syserr/BUILD +++ b/pkg/syserr/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "syserr", diff --git a/pkg/syserror/BUILD b/pkg/syserror/BUILD index d4c6da97a..ac478d0ff 100644 --- a/pkg/syserror/BUILD +++ b/pkg/syserror/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "syserror", diff --git a/pkg/tcpip/BUILD b/pkg/tcpip/BUILD index daff9a0a0..83524cc8a 100644 --- a/pkg/tcpip/BUILD +++ b/pkg/tcpip/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/tcpip/adapters/gonet/BUILD b/pkg/tcpip/adapters/gonet/BUILD index 723ad668f..ee2417238 100644 --- a/pkg/tcpip/adapters/gonet/BUILD +++ b/pkg/tcpip/adapters/gonet/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "gonet", diff --git a/pkg/tcpip/buffer/BUILD b/pkg/tcpip/buffer/BUILD index 11a725423..648d12cdf 100644 --- a/pkg/tcpip/buffer/BUILD +++ b/pkg/tcpip/buffer/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/tcpip/checker/BUILD b/pkg/tcpip/checker/BUILD index a1de808b9..f597d0b24 100644 --- a/pkg/tcpip/checker/BUILD +++ b/pkg/tcpip/checker/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "checker", diff --git a/pkg/tcpip/hash/jenkins/BUILD b/pkg/tcpip/hash/jenkins/BUILD index bbb764db8..ce2194a4d 100644 --- a/pkg/tcpip/hash/jenkins/BUILD +++ b/pkg/tcpip/hash/jenkins/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "jenkins", diff --git a/pkg/tcpip/header/BUILD b/pkg/tcpip/header/BUILD index 8e455fe1e..a5c7290ee 100644 --- a/pkg/tcpip/header/BUILD +++ b/pkg/tcpip/header/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/tcpip/link/channel/BUILD b/pkg/tcpip/link/channel/BUILD index 25f6c1457..ae285e495 100644 --- a/pkg/tcpip/link/channel/BUILD +++ b/pkg/tcpip/link/channel/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "channel", diff --git a/pkg/tcpip/link/fdbased/BUILD b/pkg/tcpip/link/fdbased/BUILD index a4aa3feec..0d78c9b15 100644 --- a/pkg/tcpip/link/fdbased/BUILD +++ b/pkg/tcpip/link/fdbased/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "fdbased", diff --git a/pkg/tcpip/link/loopback/BUILD b/pkg/tcpip/link/loopback/BUILD index a46ba7f11..710a05ede 100644 --- a/pkg/tcpip/link/loopback/BUILD +++ b/pkg/tcpip/link/loopback/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "loopback", diff --git a/pkg/tcpip/link/rawfile/BUILD b/pkg/tcpip/link/rawfile/BUILD index 2746d4ced..f01bb2c07 100644 --- a/pkg/tcpip/link/rawfile/BUILD +++ b/pkg/tcpip/link/rawfile/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "rawfile", diff --git a/pkg/tcpip/link/sharedmem/BUILD b/pkg/tcpip/link/sharedmem/BUILD index d7f1e66ef..dc8f1543e 100644 --- a/pkg/tcpip/link/sharedmem/BUILD +++ b/pkg/tcpip/link/sharedmem/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "sharedmem", diff --git a/pkg/tcpip/link/sharedmem/pipe/BUILD b/pkg/tcpip/link/sharedmem/pipe/BUILD index 12e813509..85deafa38 100644 --- a/pkg/tcpip/link/sharedmem/pipe/BUILD +++ b/pkg/tcpip/link/sharedmem/pipe/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "pipe", diff --git a/pkg/tcpip/link/sharedmem/queue/BUILD b/pkg/tcpip/link/sharedmem/queue/BUILD index 661037bb2..d7dc631eb 100644 --- a/pkg/tcpip/link/sharedmem/queue/BUILD +++ b/pkg/tcpip/link/sharedmem/queue/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "queue", diff --git a/pkg/tcpip/link/sniffer/BUILD b/pkg/tcpip/link/sniffer/BUILD index 52e237c25..7d0d1781e 100644 --- a/pkg/tcpip/link/sniffer/BUILD +++ b/pkg/tcpip/link/sniffer/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "sniffer", diff --git a/pkg/tcpip/link/tun/BUILD b/pkg/tcpip/link/tun/BUILD index 5ec01cec9..e54852d3f 100644 --- a/pkg/tcpip/link/tun/BUILD +++ b/pkg/tcpip/link/tun/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "tun", diff --git a/pkg/tcpip/link/waitable/BUILD b/pkg/tcpip/link/waitable/BUILD index ba495c437..89a9eee23 100644 --- a/pkg/tcpip/link/waitable/BUILD +++ b/pkg/tcpip/link/waitable/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "waitable", diff --git a/pkg/tcpip/network/BUILD b/pkg/tcpip/network/BUILD index a2a07f533..f36f49453 100644 --- a/pkg/tcpip/network/BUILD +++ b/pkg/tcpip/network/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_test( name = "ip_test", diff --git a/pkg/tcpip/network/arp/BUILD b/pkg/tcpip/network/arp/BUILD index f6fb7daf7..ef18bb93d 100644 --- a/pkg/tcpip/network/arp/BUILD +++ b/pkg/tcpip/network/arp/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "arp", diff --git a/pkg/tcpip/network/fragmentation/BUILD b/pkg/tcpip/network/fragmentation/BUILD index aaabfcb9a..bf0a7b99c 100644 --- a/pkg/tcpip/network/fragmentation/BUILD +++ b/pkg/tcpip/network/fragmentation/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/tcpip/network/hash/BUILD b/pkg/tcpip/network/hash/BUILD index 401dce646..ea520c6ed 100644 --- a/pkg/tcpip/network/hash/BUILD +++ b/pkg/tcpip/network/hash/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "hash", diff --git a/pkg/tcpip/network/ipv4/BUILD b/pkg/tcpip/network/ipv4/BUILD index e72317e9f..7a5341def 100644 --- a/pkg/tcpip/network/ipv4/BUILD +++ b/pkg/tcpip/network/ipv4/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "ipv4", diff --git a/pkg/tcpip/network/ipv6/BUILD b/pkg/tcpip/network/ipv6/BUILD index 808c37df3..000e00dba 100644 --- a/pkg/tcpip/network/ipv6/BUILD +++ b/pkg/tcpip/network/ipv6/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "ipv6", diff --git a/pkg/tcpip/ports/BUILD b/pkg/tcpip/ports/BUILD index a2fa9b84a..3ee80c62b 100644 --- a/pkg/tcpip/ports/BUILD +++ b/pkg/tcpip/ports/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "ports", diff --git a/pkg/tcpip/sample/tun_tcp_connect/BUILD b/pkg/tcpip/sample/tun_tcp_connect/BUILD index 32baf2115..996939581 100644 --- a/pkg/tcpip/sample/tun_tcp_connect/BUILD +++ b/pkg/tcpip/sample/tun_tcp_connect/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "tun_tcp_connect", diff --git a/pkg/tcpip/sample/tun_tcp_echo/BUILD b/pkg/tcpip/sample/tun_tcp_echo/BUILD index 760445843..dad8ef399 100644 --- a/pkg/tcpip/sample/tun_tcp_echo/BUILD +++ b/pkg/tcpip/sample/tun_tcp_echo/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "tun_tcp_echo", diff --git a/pkg/tcpip/seqnum/BUILD b/pkg/tcpip/seqnum/BUILD index c5c889239..a63665efc 100644 --- a/pkg/tcpip/seqnum/BUILD +++ b/pkg/tcpip/seqnum/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/tcpip/stack/BUILD b/pkg/tcpip/stack/BUILD index 8a598c57d..551c3c73e 100644 --- a/pkg/tcpip/stack/BUILD +++ b/pkg/tcpip/stack/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/tcpip/transport/ping/BUILD b/pkg/tcpip/transport/ping/BUILD index 982b6795c..4d4241d4b 100644 --- a/pkg/tcpip/transport/ping/BUILD +++ b/pkg/tcpip/transport/ping/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library") diff --git a/pkg/tcpip/transport/tcp/BUILD b/pkg/tcpip/transport/tcp/BUILD index 726107739..e5c05f8c0 100644 --- a/pkg/tcpip/transport/tcp/BUILD +++ b/pkg/tcpip/transport/tcp/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/tcpip/transport/tcp/testing/context/BUILD b/pkg/tcpip/transport/tcp/testing/context/BUILD index 814e5c1ea..1584e4095 100644 --- a/pkg/tcpip/transport/tcp/testing/context/BUILD +++ b/pkg/tcpip/transport/tcp/testing/context/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "context", diff --git a/pkg/tcpip/transport/tcpconntrack/BUILD b/pkg/tcpip/transport/tcpconntrack/BUILD index ac1a94d4d..31a845dee 100644 --- a/pkg/tcpip/transport/tcpconntrack/BUILD +++ b/pkg/tcpip/transport/tcpconntrack/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "tcpconntrack", diff --git a/pkg/tcpip/transport/udp/BUILD b/pkg/tcpip/transport/udp/BUILD index 4225e28dc..8ccb79c48 100644 --- a/pkg/tcpip/transport/udp/BUILD +++ b/pkg/tcpip/transport/udp/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template_instance") load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/pkg/tmutex/BUILD b/pkg/tmutex/BUILD index c20df7005..69035044d 100644 --- a/pkg/tmutex/BUILD +++ b/pkg/tmutex/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "tmutex", diff --git a/pkg/unet/BUILD b/pkg/unet/BUILD index f90e43c89..5e177e78e 100644 --- a/pkg/unet/BUILD +++ b/pkg/unet/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "unet", diff --git a/pkg/urpc/BUILD b/pkg/urpc/BUILD index 21008cf6c..36cae67e1 100644 --- a/pkg/urpc/BUILD +++ b/pkg/urpc/BUILD @@ -1,6 +1,6 @@ load("//tools/go_stateify:defs.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "urpc", diff --git a/pkg/waiter/BUILD b/pkg/waiter/BUILD index 5e611c54f..b748246da 100644 --- a/pkg/waiter/BUILD +++ b/pkg/waiter/BUILD @@ -1,4 +1,4 @@ -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_stateify:defs.bzl", "go_library", "go_test") diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index 15a7cdae1..540e99151 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "boot", diff --git a/runsc/boot/filter/BUILD b/runsc/boot/filter/BUILD index 004222242..3b6020cf3 100644 --- a/runsc/boot/filter/BUILD +++ b/runsc/boot/filter/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "filter", diff --git a/runsc/cgroup/BUILD b/runsc/cgroup/BUILD index 4f9a25a25..620d33a19 100644 --- a/runsc/cgroup/BUILD +++ b/runsc/cgroup/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "cgroup", diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index a908172af..9e2be0d37 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "cmd", diff --git a/runsc/console/BUILD b/runsc/console/BUILD index ff4ccff69..3ff9eba27 100644 --- a/runsc/console/BUILD +++ b/runsc/console/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "console", diff --git a/runsc/container/BUILD b/runsc/container/BUILD index 354ce2661..3b25ff79a 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "container", diff --git a/runsc/fsgofer/BUILD b/runsc/fsgofer/BUILD index 756c20ad7..4adc9c1bc 100644 --- a/runsc/fsgofer/BUILD +++ b/runsc/fsgofer/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "fsgofer", diff --git a/runsc/fsgofer/filter/BUILD b/runsc/fsgofer/filter/BUILD index c7848d10c..78c5b526c 100644 --- a/runsc/fsgofer/filter/BUILD +++ b/runsc/fsgofer/filter/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "filter", diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index 899fd99de..2ed793333 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "sandbox", diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 77a10e2b6..372799850 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "specutils", diff --git a/runsc/test/image/BUILD b/runsc/test/image/BUILD index 22b3ebd2a..e8b629c6a 100644 --- a/runsc/test/image/BUILD +++ b/runsc/test/image/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_test( name = "image_test", diff --git a/runsc/test/integration/BUILD b/runsc/test/integration/BUILD index e7204dc66..779d30ec9 100644 --- a/runsc/test/integration/BUILD +++ b/runsc/test/integration/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_test( name = "integration_test", diff --git a/runsc/test/root/BUILD b/runsc/test/root/BUILD index 77dcbd79e..75826a521 100644 --- a/runsc/test/root/BUILD +++ b/runsc/test/root/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "root", diff --git a/runsc/test/root/testdata/BUILD b/runsc/test/root/testdata/BUILD index 6c9fe0aea..7f272dcd3 100644 --- a/runsc/test/root/testdata/BUILD +++ b/runsc/test/root/testdata/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "testdata", diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index 8c3919320..ddec81444 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "testutil", diff --git a/runsc/tools/dockercfg/BUILD b/runsc/tools/dockercfg/BUILD index a80b3abab..fd406ab93 100644 --- a/runsc/tools/dockercfg/BUILD +++ b/runsc/tools/dockercfg/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "dockercfg", diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 8c391c8a6..148d9c366 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -1,7 +1,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") load("//test/syscalls:build_defs.bzl", "syscall_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) syscall_test(test = "//test/syscalls/linux:32bit_test") diff --git a/test/syscalls/gtest/BUILD b/test/syscalls/gtest/BUILD index d078fd3d5..22e061652 100644 --- a/test/syscalls/gtest/BUILD +++ b/test/syscalls/gtest/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "gtest", diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index e70742875..a311ca12c 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1,6 +1,6 @@ package( default_visibility = ["//:sandbox"], - licenses = ["notice"], # Apache 2.0 + licenses = ["notice"], ) cc_binary( diff --git a/test/util/BUILD b/test/util/BUILD index f2e563507..fac0730b4 100644 --- a/test/util/BUILD +++ b/test/util/BUILD @@ -1,6 +1,6 @@ package( default_visibility = ["//:sandbox"], - licenses = ["notice"], # Apache 2.0 + licenses = ["notice"], ) cc_library( diff --git a/tools/go_generics/BUILD b/tools/go_generics/BUILD index 2d97d99dc..39318b877 100644 --- a/tools/go_generics/BUILD +++ b/tools/go_generics/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "go_generics", diff --git a/tools/go_generics/globals/BUILD b/tools/go_generics/globals/BUILD index c26ac56d2..6628132f5 100644 --- a/tools/go_generics/globals/BUILD +++ b/tools/go_generics/globals/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_library( name = "globals", diff --git a/tools/go_generics/go_merge/BUILD b/tools/go_generics/go_merge/BUILD index a60437962..02b09120e 100644 --- a/tools/go_generics/go_merge/BUILD +++ b/tools/go_generics/go_merge/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "go_merge", diff --git a/tools/go_generics/rules_tests/BUILD b/tools/go_generics/rules_tests/BUILD index 23b2d656d..a6f8cdd3c 100644 --- a/tools/go_generics/rules_tests/BUILD +++ b/tools/go_generics/rules_tests/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_test") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance") diff --git a/tools/go_stateify/BUILD b/tools/go_stateify/BUILD index 68d37f5d7..bb53f8ae9 100644 --- a/tools/go_stateify/BUILD +++ b/tools/go_stateify/BUILD @@ -1,6 +1,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) go_binary( name = "stateify", diff --git a/vdso/BUILD b/vdso/BUILD index fd395511c..c43d24070 100644 --- a/vdso/BUILD +++ b/vdso/BUILD @@ -3,7 +3,7 @@ # normal system VDSO (time, gettimeofday, clock_gettimeofday) but which uses # timekeeping parameters managed by the sandbox kernel. -package(licenses = ["notice"]) # Apache 2.0 +package(licenses = ["notice"]) genrule( name = "vdso", -- cgit v1.2.3 From e420cc3e5d2066674d32d16ad885bee6b30da210 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Mon, 18 Mar 2019 12:29:43 -0700 Subject: Add support for mount propagation Properly handle propagation options for root and mounts. Now usage of mount options shared, rshared, and noexec cause error to start. shared/ rshared breaks sandbox=>host isolation. slave however can be supported because changes propagate from host to sandbox. Root FS setup moved inside the gofer. Apart from simplifying the code, it keeps all mounts inside the namespace. And they are torn down when the namespace is destroyed (DestroyFS is no longer needed). PiperOrigin-RevId: 239037661 Change-Id: I8b5ee4d50da33c042ea34fa68e56514ebe20e6e0 --- runsc/cmd/BUILD | 1 + runsc/cmd/boot.go | 16 +++ runsc/cmd/gofer.go | 279 +++++++++++++++++++++++++++++++----- runsc/cmd/gofer_test.go | 164 ++++++++++++++++++++++ runsc/container/BUILD | 2 - runsc/container/container.go | 75 ++++------ runsc/container/container_test.go | 165 ++++++++++++++++++++++ runsc/container/fs.go | 287 -------------------------------------- runsc/container/fs_test.go | 158 --------------------- runsc/sandbox/sandbox.go | 23 +-- runsc/specutils/BUILD | 1 + runsc/specutils/fs.go | 139 ++++++++++++++++++ runsc/specutils/namespace.go | 16 +-- runsc/specutils/specutils.go | 52 ++++--- runsc/specutils/specutils_test.go | 31 ++++ 15 files changed, 834 insertions(+), 575 deletions(-) create mode 100644 runsc/cmd/gofer_test.go delete mode 100644 runsc/container/fs.go delete mode 100644 runsc/container/fs_test.go create mode 100644 runsc/specutils/fs.go (limited to 'runsc/specutils') diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 9e2be0d37..dabf18c5f 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -60,6 +60,7 @@ go_test( "capability_test.go", "delete_test.go", "exec_test.go", + "gofer_test.go", ], data = [ "//runsc", diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 3039b389f..ff2fa2fb9 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -76,6 +76,11 @@ type Boot struct { // startSyncFD is the file descriptor to synchronize runsc and sandbox. startSyncFD int + // mountsFD is the file descriptor to read list of mounts after they have + // been resolved (direct paths, no symlinks). They are resolved outside the + // sandbox (e.g. gofer) and sent through this FD. + mountsFD int + // pidns is set if the sanadbox is in its own pid namespace. pidns bool } @@ -111,6 +116,7 @@ func (b *Boot) SetFlags(f *flag.FlagSet) { f.Uint64Var(&b.totalMem, "total-memory", 0, "sets the initial amount of total memory to report back to the container") 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") + f.IntVar(&b.mountsFD, "mounts-fd", -1, "mountsFD is the file descriptor to read list of mounts after they have been resolved (direct paths, no symlinks).") } // Execute implements subcommands.Command.Execute. It starts a sandbox in a @@ -191,6 +197,16 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) panic("setCapsAndCallSelf must never return success") } + // Read resolved mount list and replace the original one from the spec. + mountsFile := os.NewFile(uintptr(b.mountsFD), "mounts file") + cleanMounts, err := specutils.ReadMounts(mountsFile) + if err != nil { + mountsFile.Close() + Fatalf("Error reading mounts file: %v", err) + } + mountsFile.Close() + spec.Mounts = cleanMounts + // Create the loader. bootArgs := boot.Args{ ID: f.Arg(0), diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index 6f9711518..e712244ef 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -16,7 +16,11 @@ package cmd import ( "context" + "encoding/json" + "fmt" "os" + "path/filepath" + "strings" "sync" "syscall" @@ -59,6 +63,7 @@ type Gofer struct { panicOnWrite bool specFD int + mountsFD int } // Name implements subcommands.Command. @@ -84,6 +89,7 @@ func (g *Gofer) SetFlags(f *flag.FlagSet) { f.BoolVar(&g.panicOnWrite, "panic-on-write", false, "if true, panics on attempts to write to RO mounts. RW mounts are unnaffected") f.BoolVar(&g.setUpRoot, "setup-root", true, "if true, set up an empty root for the process") f.IntVar(&g.specFD, "spec-fd", -1, "required fd with the container spec") + f.IntVar(&g.mountsFD, "mounts-fd", -1, "mountsFD is the file descriptor to write list of mounts after they have been resolved (direct paths, no symlinks).") } // Execute implements subcommands.Command. @@ -100,45 +106,13 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) Fatalf("reading spec: %v", err) } - // Find what path is going to be served by this gofer. - root := spec.Root.Path - conf := args[0].(*boot.Config) - if g.setUpRoot && !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { - // Convert all shared mounts into slave to be sure that nothing will be - // propagated outside of our namespace. - if err := syscall.Mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil { - Fatalf("error converting mounts: %v", err) - } - - // FIXME: runsc can't be re-executed without - // /proc, so we create a tmpfs mount, mount ./proc and ./root - // there, then move this mount to the root and after - // setCapsAndCallSelf, runsc will chroot into /root. - // - // We need a directory to construct a new root and we know that - // runsc can't start without /proc, so we can use it for this. - flags := uintptr(syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC) - if err := syscall.Mount("runsc-root", "/proc", "tmpfs", flags, ""); err != nil { - Fatalf("error mounting tmpfs: %v", err) - } - os.Mkdir("/proc/proc", 0755) - os.Mkdir("/proc/root", 0755) - if err := syscall.Mount("runsc-proc", "/proc/proc", "proc", flags|syscall.MS_RDONLY, ""); err != nil { - Fatalf("error mounting proc: %v", err) - } - if err := syscall.Mount(root, "/proc/root", "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { - Fatalf("error mounting root: %v", err) - } - if err := pivotRoot("/proc"); err != nil { - Fatalf("faild to change the root file system: %v", err) - } - if err := os.Chdir("/"); err != nil { - Fatalf("failed to change working directory") + if g.setUpRoot { + if err := setupRootFS(spec, conf); err != nil { + Fatalf("Error setting up root FS: %v", err) } } - if g.applyCaps { // Disable caps when calling myself again. // Note: minimal argument handling for the default case to keep it simple. @@ -150,15 +124,34 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) panic("unreachable") } + // Find what path is going to be served by this gofer. + root := spec.Root.Path + if !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { + root = "/root" + } + + // Resolve mount points paths, then replace mounts from our spec and send the + // mount list over to the sandbox, so they are both in sync. + // + // Note that all mount points have been mounted in the proper location in + // setupRootFS(). + cleanMounts, err := resolveMounts(spec.Mounts, root) + if err != nil { + Fatalf("Failure to resolve mounts: %v", err) + } + spec.Mounts = cleanMounts + go func() { + if err := g.writeMounts(cleanMounts); err != nil { + panic(fmt.Sprintf("Failed to write mounts: %v", err)) + } + }() + specutils.LogSpec(spec) // fsgofer should run with a umask of 0, because we want to preserve file // modes exactly as sent by the sandbox, which will have applied its own umask. syscall.Umask(0) - if !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { - root = "/root" - } if err := syscall.Chroot(root); err != nil { Fatalf("failed to chroot to %q: %v", root, err) } @@ -232,6 +225,25 @@ func runServers(ats []p9.Attacher, ioFDs []int) { log.Infof("All 9P servers exited.") } +func (g *Gofer) writeMounts(mounts []specs.Mount) error { + bytes, err := json.Marshal(mounts) + if err != nil { + return err + } + + f := os.NewFile(uintptr(g.mountsFD), "mounts file") + defer f.Close() + + for written := 0; written < len(bytes); { + w, err := f.Write(bytes[written:]) + if err != nil { + return err + } + written += w + } + return nil +} + func isReadonlyMount(opts []string) bool { for _, o := range opts { if o == "ro" { @@ -240,3 +252,194 @@ func isReadonlyMount(opts []string) bool { } return false } + +func setupRootFS(spec *specs.Spec, conf *boot.Config) error { + // Convert all shared mounts into slaves to be sure that nothing will be + // propagated outside of our namespace. + if err := syscall.Mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil { + Fatalf("error converting mounts: %v", err) + } + + root := spec.Root.Path + if !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { + // FIXME: runsc can't be re-executed without + // /proc, so we create a tmpfs mount, mount ./proc and ./root + // there, then move this mount to the root and after + // setCapsAndCallSelf, runsc will chroot into /root. + // + // We need a directory to construct a new root and we know that + // runsc can't start without /proc, so we can use it for this. + flags := uintptr(syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC) + if err := syscall.Mount("runsc-root", "/proc", "tmpfs", flags, ""); err != nil { + Fatalf("error mounting tmpfs: %v", err) + } + + // Prepare tree structure for pivot_root(2). + os.Mkdir("/proc/proc", 0755) + os.Mkdir("/proc/root", 0755) + if err := syscall.Mount("runsc-proc", "/proc/proc", "proc", flags|syscall.MS_RDONLY, ""); err != nil { + Fatalf("error mounting proc: %v", err) + } + root = "/proc/root" + } + + // Mount root path followed by submounts. + if err := syscall.Mount(spec.Root.Path, root, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { + return fmt.Errorf("mounting root on root (%q) err: %v", spec.Root.Path, err) + } + flags := uint32(syscall.MS_SLAVE | syscall.MS_REC) + if spec.Linux != nil && spec.Linux.RootfsPropagation != "" { + flags = specutils.PropOptionsToFlags([]string{spec.Linux.RootfsPropagation}) + } + if err := syscall.Mount("", spec.Root.Path, "", uintptr(flags), ""); err != nil { + return fmt.Errorf("mounting root (%q) with flags: %#x, err: %v", spec.Root.Path, flags, err) + } + + // Replace the current spec, with the clean spec with symlinks resolved. + if err := setupMounts(spec.Mounts, root); err != nil { + Fatalf("error setting up FS: %v", err) + } + + // Create working directory if needed. + if spec.Process.Cwd != "" { + dst, err := resolveSymlinks(root, spec.Process.Cwd) + if err != nil { + return fmt.Errorf("resolving symlinks to %q: %v", spec.Process.Cwd, err) + } + if err := os.MkdirAll(dst, 0755); err != nil { + return fmt.Errorf("creating working directory %q: %v", spec.Process.Cwd, err) + } + } + + // Check if root needs to be remounted as readonly. + if spec.Root.Readonly { + // If root is a mount point but not read-only, we can change mount options + // to make it read-only for extra safety. + log.Infof("Remounting root as readonly: %q", spec.Root.Path) + flags := uintptr(syscall.MS_BIND | syscall.MS_REMOUNT | syscall.MS_RDONLY | syscall.MS_REC) + if err := syscall.Mount(spec.Root.Path, spec.Root.Path, "bind", flags, ""); err != nil { + return fmt.Errorf("remounting root as read-only with source: %q, target: %q, flags: %#x, err: %v", spec.Root.Path, spec.Root.Path, flags, err) + } + } + + if !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { + if err := pivotRoot("/proc"); err != nil { + Fatalf("faild to change the root file system: %v", err) + } + if err := os.Chdir("/"); err != nil { + Fatalf("failed to change working directory") + } + } + return nil +} + +// setupMounts binds mount all mounts specified in the spec in their correct +// location inside root. It will resolve relative paths and symlinks. It also +// creates directories as needed. +func setupMounts(mounts []specs.Mount, root string) error { + for _, m := range mounts { + if m.Type != "bind" || !specutils.IsSupportedDevMount(m) { + continue + } + + dst, err := resolveSymlinks(root, m.Destination) + if err != nil { + return fmt.Errorf("resolving symlinks to %q: %v", m.Destination, err) + } + + flags := specutils.OptionsToFlags(m.Options) | syscall.MS_BIND + log.Infof("Mounting src: %q, dst: %q, flags: %#x", m.Source, dst, flags) + if err := specutils.Mount(m.Source, dst, m.Type, flags); err != nil { + return fmt.Errorf("mounting %v: %v", m, err) + } + + // Set propagation options that cannot be set together with other options. + flags = specutils.PropOptionsToFlags(m.Options) + if flags != 0 { + if err := syscall.Mount("", dst, "", uintptr(flags), ""); err != nil { + return fmt.Errorf("mount dst: %q, flags: %#x, err: %v", dst, flags, err) + } + } + } + return nil +} + +// resolveMounts resolved relative paths and symlinks to mount points. +// +// Note: mount points must already be in place for resolution to work. +// Otherwise, it may follow symlinks to locations that would be overwritten +// with another mount point and return the wrong location. In short, make sure +// setupMounts() has been called before. +func resolveMounts(mounts []specs.Mount, root string) ([]specs.Mount, error) { + cleanMounts := make([]specs.Mount, 0, len(mounts)) + for _, m := range mounts { + if m.Type != "bind" || !specutils.IsSupportedDevMount(m) { + cleanMounts = append(cleanMounts, m) + continue + } + dst, err := resolveSymlinks(root, m.Destination) + if err != nil { + return nil, fmt.Errorf("resolving symlinks to %q: %v", m.Destination, err) + } + relDst, err := filepath.Rel(root, dst) + if err != nil { + panic(fmt.Sprintf("%q could not be made relative to %q: %v", dst, root, err)) + } + cpy := m + cpy.Destination = filepath.Join("/", relDst) + cleanMounts = append(cleanMounts, cpy) + } + return cleanMounts, nil +} + +// ResolveSymlinks walks 'rel' having 'root' as the root directory. If there are +// symlinks, they are evaluated relative to 'root' to ensure the end result is +// the same as if the process was running inside the container. +func resolveSymlinks(root, rel string) (string, error) { + return resolveSymlinksImpl(root, root, rel, 255) +} + +func resolveSymlinksImpl(root, base, rel string, followCount uint) (string, error) { + if followCount == 0 { + return "", fmt.Errorf("too many symlinks to follow, path: %q", filepath.Join(base, rel)) + } + + rel = filepath.Clean(rel) + for _, name := range strings.Split(rel, string(filepath.Separator)) { + if name == "" { + continue + } + // Note that Join() resolves things like ".." and returns a clean path. + path := filepath.Join(base, name) + if !strings.HasPrefix(path, root) { + // One cannot '..' their way out of root. + path = root + continue + } + fi, err := os.Lstat(path) + if err != nil { + if !os.IsNotExist(err) { + return "", err + } + // Not found means there is no symlink to check. Just keep walking dirs. + base = path + continue + } + if fi.Mode()&os.ModeSymlink != 0 { + link, err := os.Readlink(path) + if err != nil { + return "", err + } + if filepath.IsAbs(link) { + base = root + } + base, err = resolveSymlinksImpl(root, base, link, followCount-1) + if err != nil { + return "", err + } + continue + } + base = path + } + return base, nil +} diff --git a/runsc/cmd/gofer_test.go b/runsc/cmd/gofer_test.go new file mode 100644 index 000000000..8e692feb9 --- /dev/null +++ b/runsc/cmd/gofer_test.go @@ -0,0 +1,164 @@ +// Copyright 2018 Google LLC +// +// 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" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" +) + +func tmpDir() string { + dir := os.Getenv("TEST_TMPDIR") + if dir == "" { + dir = "/tmp" + } + return dir +} + +type dir struct { + rel string + link string +} + +func construct(root string, dirs []dir) error { + for _, d := range dirs { + p := path.Join(root, d.rel) + if d.link == "" { + if err := os.MkdirAll(p, 0755); err != nil { + return fmt.Errorf("error creating dir: %v", err) + } + } else { + if err := os.MkdirAll(path.Dir(p), 0755); err != nil { + return fmt.Errorf("error creating dir: %v", err) + } + if err := os.Symlink(d.link, p); err != nil { + return fmt.Errorf("error creating symlink: %v", err) + } + } + } + return nil +} + +func TestResolveSymlinks(t *testing.T) { + root, err := ioutil.TempDir(tmpDir(), "root") + if err != nil { + t.Fatal("ioutil.TempDir() failed:", err) + } + dirs := []dir{ + {"dir1/dir11/dir111/dir1111", ""}, // Just a boring dir + {"dir1/lnk12", "dir11"}, // Link to sibling + {"dir1/lnk13", "./dir11"}, // Link to sibling through self + {"dir1/lnk14", "../dir1/dir11"}, // Link to sibling through parent + {"dir1/dir15/lnk151", ".."}, // Link to parent + {"dir1/lnk16", "dir11/dir111"}, // Link to child + {"dir1/lnk17", "."}, // Link to self + {"dir1/lnk18", "lnk13"}, // Link to link + {"lnk2", "dir1/lnk13"}, // Link to link to link + {"dir3/dir21/lnk211", "../.."}, // Link to root relative + {"dir3/lnk22", "/"}, // Link to root absolute + {"dir3/lnk23", "/dir1"}, // Link to dir absolute + {"dir3/lnk24", "/dir1/lnk12"}, // Link to link absolute + {"lnk5", "../../.."}, // Link outside root + } + if err := construct(root, dirs); err != nil { + t.Fatal("construct failed:", err) + } + + tests := []struct { + name string + rel string + want string + compareHost bool + }{ + {name: "root", rel: "/", want: "/", compareHost: true}, + {name: "basic dir", rel: "/dir1/dir11/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "dot 1", rel: "/dir1/dir11/./dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "dot 2", rel: "/dir1/././dir11/./././././dir111/.", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "dotdot 1", rel: "/dir1/dir11/../dir15", want: "/dir1/dir15", compareHost: true}, + {name: "dotdot 2", rel: "/dir1/dir11/dir1111/../..", want: "/dir1", compareHost: true}, + + {name: "link sibling", rel: "/dir1/lnk12", want: "/dir1/dir11", compareHost: true}, + {name: "link sibling + dir", rel: "/dir1/lnk12/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "link sibling through self", rel: "/dir1/lnk13", want: "/dir1/dir11", compareHost: true}, + {name: "link sibling through parent", rel: "/dir1/lnk14", want: "/dir1/dir11", compareHost: true}, + + {name: "link parent", rel: "/dir1/dir15/lnk151", want: "/dir1", compareHost: true}, + {name: "link parent + dir", rel: "/dir1/dir15/lnk151/dir11", want: "/dir1/dir11", compareHost: true}, + {name: "link child", rel: "/dir1/lnk16", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "link child + dir", rel: "/dir1/lnk16/dir1111", want: "/dir1/dir11/dir111/dir1111", compareHost: true}, + {name: "link self", rel: "/dir1/lnk17", want: "/dir1", compareHost: true}, + {name: "link self + dir", rel: "/dir1/lnk17/dir11", want: "/dir1/dir11", compareHost: true}, + + {name: "link^2", rel: "/dir1/lnk18", want: "/dir1/dir11", compareHost: true}, + {name: "link^2 + dir", rel: "/dir1/lnk18/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + {name: "link^3", rel: "/lnk2", want: "/dir1/dir11", compareHost: true}, + {name: "link^3 + dir", rel: "/lnk2/dir111", want: "/dir1/dir11/dir111", compareHost: true}, + + {name: "link abs", rel: "/dir3/lnk23", want: "/dir1"}, + {name: "link abs + dir", rel: "/dir3/lnk23/dir11", want: "/dir1/dir11"}, + {name: "link^2 abs", rel: "/dir3/lnk24", want: "/dir1/dir11"}, + {name: "link^2 abs + dir", rel: "/dir3/lnk24/dir111", want: "/dir1/dir11/dir111"}, + + {name: "root link rel", rel: "/dir3/dir21/lnk211", want: "/", compareHost: true}, + {name: "root link abs", rel: "/dir3/lnk22", want: "/"}, + {name: "root contain link", rel: "/lnk5/dir1", want: "/dir1"}, + {name: "root contain dotdot", rel: "/dir1/dir11/../../../../../../../..", want: "/"}, + + {name: "crazy", rel: "/dir3/dir21/lnk211/dir3/lnk22/dir1/dir11/../../lnk5/dir3/../dir3/lnk24/dir111/dir1111/..", want: "/dir1/dir11/dir111"}, + } + for _, tst := range tests { + t.Run(tst.name, func(t *testing.T) { + got, err := resolveSymlinks(root, tst.rel) + if err != nil { + t.Errorf("resolveSymlinks(root, %q) failed: %v", tst.rel, err) + } + want := path.Join(root, tst.want) + if got != want { + t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, got, want) + } + if tst.compareHost { + // Check that host got to the same end result. + host, err := filepath.EvalSymlinks(path.Join(root, tst.rel)) + if err != nil { + t.Errorf("path.EvalSymlinks(root, %q) failed: %v", tst.rel, err) + } + if host != got { + t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, host, got) + } + } + }) + } +} + +func TestResolveSymlinksLoop(t *testing.T) { + root, err := ioutil.TempDir(tmpDir(), "root") + if err != nil { + t.Fatal("ioutil.TempDir() failed:", err) + } + dirs := []dir{ + {"loop1", "loop2"}, + {"loop2", "loop1"}, + } + if err := construct(root, dirs); err != nil { + t.Fatal("construct failed:", err) + } + if _, err := resolveSymlinks(root, "loop1"); err == nil { + t.Errorf("resolveSymlinks() should have failed") + } +} diff --git a/runsc/container/BUILD b/runsc/container/BUILD index 3b25ff79a..2936b7cdf 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -6,7 +6,6 @@ go_library( name = "container", srcs = [ "container.go", - "fs.go", "hook.go", "status.go", ], @@ -34,7 +33,6 @@ go_test( srcs = [ "console_test.go", "container_test.go", - "fs_test.go", "multi_container_test.go", "shared_volume_test.go", ], diff --git a/runsc/container/container.go b/runsc/container/container.go index 6f092a5ce..fdcf8d7b7 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -281,18 +281,6 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo if specutils.ShouldCreateSandbox(spec) { log.Debugf("Creating new sandbox for container %q", id) - // Setup rootfs and mounts. It returns a new mount list with destination - // paths resolved. Since the spec for the root container is read from disk, - // Write the new spec to a new file that will be used by the sandbox. - cleanMounts, err := setupFS(spec, conf, bundleDir) - if err != nil { - return nil, fmt.Errorf("setup mounts: %v", err) - } - spec.Mounts = cleanMounts - if err := specutils.WriteCleanSpec(bundleDir, spec); err != nil { - return nil, fmt.Errorf("writing clean spec: %v", err) - } - // Create and join cgroup before processes are created to ensure they are // part of the cgroup from the start (and all tneir children processes). cg, err := cgroup.New(spec) @@ -306,14 +294,14 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo } } if err := runInCgroup(cg, func() error { - ioFiles, err := c.createGoferProcess(spec, conf, bundleDir) + ioFiles, specFile, err := c.createGoferProcess(spec, conf, bundleDir) if err != nil { return err } // Start a new sandbox for this container. Any errors after this point // must destroy the container. - c.Sandbox, err = sandbox.New(id, spec, conf, bundleDir, consoleSocket, userLog, ioFiles, cg) + c.Sandbox, err = sandbox.New(id, spec, conf, bundleDir, consoleSocket, userLog, ioFiles, specFile, cg) return err }); err != nil { return nil, err @@ -387,26 +375,22 @@ func (c *Container) Start(conf *boot.Config) error { return err } } else { - // Setup rootfs and mounts. It returns a new mount list with destination - // paths resolved. Replace the original spec with new mount list and start - // container. - cleanMounts, err := setupFS(c.Spec, conf, c.BundleDir) - if err != nil { - return fmt.Errorf("setup mounts: %v", err) - } - c.Spec.Mounts = cleanMounts - if err := specutils.WriteCleanSpec(c.BundleDir, c.Spec); err != nil { - return fmt.Errorf("writing clean spec: %v", err) - } - // Join cgroup to strt gofer process to ensure it's part of the cgroup from // the start (and all tneir children processes). if err := runInCgroup(c.Sandbox.Cgroup, func() error { // Create the gofer process. - ioFiles, err := c.createGoferProcess(c.Spec, conf, c.BundleDir) + ioFiles, mountsFile, err := c.createGoferProcess(c.Spec, conf, c.BundleDir) if err != nil { return err } + defer mountsFile.Close() + + cleanMounts, err := specutils.ReadMounts(mountsFile) + if err != nil { + return fmt.Errorf("reading mounts file: %v", err) + } + c.Spec.Mounts = cleanMounts + return c.Sandbox.StartContainer(c.Spec, conf, c.ID, ioFiles) }); err != nil { return err @@ -665,12 +649,6 @@ func (c *Container) Destroy() error { errs = append(errs, err.Error()) } - if err := destroyFS(c.Spec); err != nil { - err = fmt.Errorf("destroying container fs: %v", err) - log.Warningf("%v", err) - errs = append(errs, err.Error()) - } - if err := os.RemoveAll(c.Root); err != nil && !os.IsNotExist(err) { err = fmt.Errorf("deleting container root directory %q: %v", c.Root, err) log.Warningf("%v", err) @@ -787,7 +765,7 @@ func (c *Container) waitForStopped() error { return backoff.Retry(op, b) } -func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]*os.File, error) { +func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]*os.File, *os.File, error) { // Start with the general config flags. args := conf.ToFlags() @@ -800,7 +778,7 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bund if conf.LogFilename != "" { logFile, err := os.OpenFile(conf.LogFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { - return nil, fmt.Errorf("opening log file %q: %v", conf.LogFilename, err) + return nil, nil, fmt.Errorf("opening log file %q: %v", conf.LogFilename, err) } defer logFile.Close() goferEnds = append(goferEnds, logFile) @@ -811,7 +789,7 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bund if conf.DebugLog != "" { debugLogFile, err := specutils.DebugLogFile(conf.DebugLog, "gofer") if err != nil { - return nil, fmt.Errorf("opening debug log file in %q: %v", conf.DebugLog, err) + return nil, nil, fmt.Errorf("opening debug log file in %q: %v", conf.DebugLog, err) } defer debugLogFile.Close() goferEnds = append(goferEnds, debugLogFile) @@ -825,30 +803,39 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bund } // Open the spec file to donate to the sandbox. - specFile, err := specutils.OpenCleanSpec(bundleDir) + specFile, err := specutils.OpenSpec(bundleDir) if err != nil { - return nil, fmt.Errorf("opening spec file: %v", err) + return nil, nil, fmt.Errorf("opening spec file: %v", err) } defer specFile.Close() goferEnds = append(goferEnds, specFile) args = append(args, "--spec-fd="+strconv.Itoa(nextFD)) nextFD++ + // Create pipe that allows gofer to send mount list to sandbox after all paths + // have been resolved. + mountsSand, mountsGofer, err := os.Pipe() + if err != nil { + return nil, nil, err + } + defer mountsGofer.Close() + goferEnds = append(goferEnds, mountsGofer) + args = append(args, fmt.Sprintf("--mounts-fd=%d", nextFD)) + nextFD++ + // Add root mount and then add any other additional mounts. mountCount := 1 - - // Add additional mounts. for _, m := range spec.Mounts { if specutils.Is9PMount(m) { mountCount++ } } - sandEnds := make([]*os.File, 0, mountCount) + sandEnds := make([]*os.File, 0, mountCount) for i := 0; i < mountCount; i++ { fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) if err != nil { - return nil, err + return nil, nil, err } sandEnds = append(sandEnds, os.NewFile(uintptr(fds[0]), "sandbox IO FD")) @@ -884,12 +871,12 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bund // Start the gofer in the given namespace. log.Debugf("Starting gofer: %s %v", binPath, args) if err := specutils.StartInNS(cmd, nss); err != nil { - return nil, err + return nil, nil, err } log.Infof("Gofer started, PID: %d", cmd.Process.Pid) c.GoferPid = cmd.Process.Pid c.goferIsChild = true - return sandEnds, nil + return sandEnds, mountsSand, nil } // changeStatus transitions from one status to another ensuring that the diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 06a25de6d..f17155175 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -1594,6 +1594,171 @@ func TestCreateWorkingDir(t *testing.T) { } } +// TestMountPropagation verifies that mount propagates to slave but not to +// private mounts. +func TestMountPropagation(t *testing.T) { + // Setup dir structure: + // - src: is mounted as shared and is used as source for both private and + // slave mounts + // - dir: will be bind mounted inside src and should propagate to slave + tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "mount") + if err != nil { + t.Fatalf("ioutil.TempDir() failed: %v", err) + } + src := filepath.Join(tmpDir, "src") + srcMnt := filepath.Join(src, "mnt") + dir := filepath.Join(tmpDir, "dir") + for _, path := range []string{src, srcMnt, dir} { + if err := os.MkdirAll(path, 0777); err != nil { + t.Fatalf("MkdirAll(%q): %v", path, err) + } + } + dirFile := filepath.Join(dir, "file") + f, err := os.Create(dirFile) + if err != nil { + t.Fatalf("os.Create(%q): %v", dirFile, err) + } + f.Close() + + // Setup src as a shared mount. + if err := syscall.Mount(src, src, "bind", syscall.MS_BIND, ""); err != nil { + t.Fatalf("mount(%q, %q, MS_BIND): %v", dir, srcMnt, err) + } + if err := syscall.Mount("", src, "", syscall.MS_SHARED, ""); err != nil { + t.Fatalf("mount(%q, MS_SHARED): %v", srcMnt, err) + } + + spec := testutil.NewSpecWithArgs("sleep", "1000") + + priv := filepath.Join(tmpDir, "priv") + slave := filepath.Join(tmpDir, "slave") + spec.Mounts = []specs.Mount{ + { + Source: src, + Destination: priv, + Type: "bind", + Options: []string{"private"}, + }, + { + Source: src, + Destination: slave, + Type: "bind", + Options: []string{"slave"}, + }, + } + + conf := testutil.TestConfig() + rootDir, bundleDir, err := testutil.SetupContainer(spec, conf) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + 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) + } + + // After the container is started, mount dir inside source and check what + // happens to both destinations. + if err := syscall.Mount(dir, srcMnt, "bind", syscall.MS_BIND, ""); err != nil { + t.Fatalf("mount(%q, %q, MS_BIND): %v", dir, srcMnt, err) + } + + // Check that mount didn't propagate to private mount. + privFile := filepath.Join(priv, "mnt", "file") + args := &control.ExecArgs{ + Filename: "/usr/bin/test", + Argv: []string{"test", "!", "-f", privFile}, + } + if ws, err := cont.executeSync(args); err != nil || ws != 0 { + t.Fatalf("exec: test ! -f %q, ws: %v, err: %v", privFile, ws, err) + } + + // Check that mount propagated to slave mount. + slaveFile := filepath.Join(slave, "mnt", "file") + args = &control.ExecArgs{ + Filename: "/usr/bin/test", + Argv: []string{"test", "-f", slaveFile}, + } + if ws, err := cont.executeSync(args); err != nil || ws != 0 { + t.Fatalf("exec: test -f %q, ws: %v, err: %v", privFile, ws, err) + } +} + +func TestMountSymlink(t *testing.T) { + for _, conf := range configs(overlay) { + t.Logf("Running test with conf: %+v", conf) + + dir, err := ioutil.TempDir(testutil.TmpDir(), "mount-symlink") + if err != nil { + t.Fatalf("ioutil.TempDir() failed: %v", err) + } + + source := path.Join(dir, "source") + target := path.Join(dir, "target") + for _, path := range []string{source, target} { + if err := os.MkdirAll(path, 0777); err != nil { + t.Fatalf("os.MkdirAll(): %v", err) + } + } + f, err := os.Create(path.Join(source, "file")) + if err != nil { + t.Fatalf("os.Create(): %v", err) + } + f.Close() + + link := path.Join(dir, "link") + if err := os.Symlink(target, link); err != nil { + t.Fatalf("os.Symlink(%q, %q): %v", target, link, err) + } + + spec := testutil.NewSpecWithArgs("/bin/sleep", "1000") + + // Mount to a symlink to ensure the mount code will follow it and mount + // at the symlink target. + spec.Mounts = append(spec.Mounts, specs.Mount{ + Type: "bind", + Destination: link, + Source: source, + }) + + rootDir, bundleDir, err := testutil.SetupContainer(spec, conf) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + 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) + } + + // Check that symlink was resolved and mount was created where the symlink + // is pointing to. + file := path.Join(target, "file") + args := &control.ExecArgs{ + Filename: "/usr/bin/test", + Argv: []string{"test", "-f", file}, + } + if ws, err := cont.executeSync(args); err != nil || ws != 0 { + t.Fatalf("exec: test -f %q, ws: %v, err: %v", file, ws, err) + } + } +} + // executeSync synchronously executes a new process. func (cont *Container) executeSync(args *control.ExecArgs) (syscall.WaitStatus, error) { pid, err := cont.Execute(args) diff --git a/runsc/container/fs.go b/runsc/container/fs.go deleted file mode 100644 index 998160487..000000000 --- a/runsc/container/fs.go +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 container - -import ( - "bufio" - "fmt" - "os" - "path/filepath" - "strings" - "syscall" - - specs "github.com/opencontainers/runtime-spec/specs-go" - "gvisor.googlesource.com/gvisor/pkg/log" - "gvisor.googlesource.com/gvisor/runsc/boot" - "gvisor.googlesource.com/gvisor/runsc/specutils" -) - -type mapping struct { - set bool - val uint32 -} - -var optionsMap = map[string]mapping{ - "acl": {set: true, val: syscall.MS_POSIXACL}, - "async": {set: false, val: syscall.MS_SYNCHRONOUS}, - "atime": {set: false, val: syscall.MS_NOATIME}, - "bind": {set: true, val: syscall.MS_BIND}, - "defaults": {set: true, val: 0}, - "dev": {set: false, val: syscall.MS_NODEV}, - "diratime": {set: false, val: syscall.MS_NODIRATIME}, - "dirsync": {set: true, val: syscall.MS_DIRSYNC}, - "exec": {set: false, val: syscall.MS_NOEXEC}, - "iversion": {set: true, val: syscall.MS_I_VERSION}, - "loud": {set: false, val: syscall.MS_SILENT}, - "mand": {set: true, val: syscall.MS_MANDLOCK}, - "noacl": {set: false, val: syscall.MS_POSIXACL}, - "noatime": {set: true, val: syscall.MS_NOATIME}, - "nodev": {set: true, val: syscall.MS_NODEV}, - "nodiratime": {set: true, val: syscall.MS_NODIRATIME}, - "noexec": {set: true, val: syscall.MS_NOEXEC}, - "noiversion": {set: false, val: syscall.MS_I_VERSION}, - "nomand": {set: false, val: syscall.MS_MANDLOCK}, - "norelatime": {set: false, val: syscall.MS_RELATIME}, - "nostrictatime": {set: false, val: syscall.MS_STRICTATIME}, - "nosuid": {set: true, val: syscall.MS_NOSUID}, - "private": {set: true, val: syscall.MS_PRIVATE}, - "rbind": {set: true, val: syscall.MS_BIND | syscall.MS_REC}, - "relatime": {set: true, val: syscall.MS_RELATIME}, - "remount": {set: true, val: syscall.MS_REMOUNT}, - "ro": {set: true, val: syscall.MS_RDONLY}, - "rprivate": {set: true, val: syscall.MS_PRIVATE | syscall.MS_REC}, - "rw": {set: false, val: syscall.MS_RDONLY}, - "silent": {set: true, val: syscall.MS_SILENT}, - "strictatime": {set: true, val: syscall.MS_STRICTATIME}, - "suid": {set: false, val: syscall.MS_NOSUID}, - "sync": {set: true, val: syscall.MS_SYNCHRONOUS}, -} - -// setupFS creates the container directory structure under 'spec.Root.Path'. -// This allows the gofer serving the containers to be chroot under this -// directory to create an extra layer to security in case the gofer gets -// compromised. -// Returns list of mounts equivalent to 'spec.Mounts' with all destination paths -// cleaned and with symlinks resolved. -func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]specs.Mount, error) { - rv := make([]specs.Mount, 0, len(spec.Mounts)) - for _, m := range spec.Mounts { - if m.Type != "bind" || !specutils.IsSupportedDevMount(m) { - rv = append(rv, m) - continue - } - - // It's possible that 'm.Destination' follows symlinks inside the - // container. - dst, err := resolveSymlinks(spec.Root.Path, m.Destination) - if err != nil { - return nil, fmt.Errorf("resolving symlinks to %q: %v", m.Destination, err) - } - - flags := optionsToFlags(m.Options) - flags |= syscall.MS_BIND - log.Infof("Mounting src: %q, dst: %q, flags: %#x", m.Source, dst, flags) - if err := specutils.Mount(m.Source, dst, m.Type, flags); err != nil { - return nil, fmt.Errorf("mounting %v: %v", m, err) - } - - // Make the mount a slave, so that for recursive bind mount, umount won't - // propagate to the source. - flags = syscall.MS_SLAVE | syscall.MS_REC - if err := syscall.Mount("", dst, "", uintptr(flags), ""); err != nil { - return nil, fmt.Errorf("mount rslave dst: %q, flags: %#x, err: %v", dst, flags, err) - } - - cpy := m - relDst, err := filepath.Rel(spec.Root.Path, dst) - if err != nil { - panic(fmt.Sprintf("%q could not be made relative to %q: %v", dst, spec.Root.Path, err)) - } - cpy.Destination = filepath.Join("/", relDst) - rv = append(rv, cpy) - } - - if spec.Process.Cwd != "" { - dst, err := resolveSymlinks(spec.Root.Path, spec.Process.Cwd) - if err != nil { - return nil, fmt.Errorf("resolving symlinks to %q: %v", spec.Process.Cwd, err) - } - if err := os.MkdirAll(dst, 0755); err != nil { - return nil, err - } - } - - // If root is read only, check if it needs to be remounted as readonly. - if spec.Root.Readonly { - isMountPoint, readonly, err := mountInfo(spec.Root.Path) - if err != nil { - return nil, err - } - if readonly { - return rv, nil - } - if !isMountPoint { - // Readonly root is not a mount point nor read-only. Can't do much other - // than just logging a warning. The gofer will prevent files to be open - // in write mode. - log.Warningf("Mount where root is located is not read-only and cannot be changed: %q", spec.Root.Path) - return rv, nil - } - - // If root is a mount point but not read-only, we can change mount options - // to make it read-only for extra safety. - log.Infof("Remounting root as readonly: %q", spec.Root.Path) - flags := uintptr(syscall.MS_BIND | syscall.MS_REMOUNT | syscall.MS_RDONLY | syscall.MS_REC) - src := spec.Root.Path - if err := syscall.Mount(src, src, "bind", flags, ""); err != nil { - return nil, fmt.Errorf("remounting root as read-only with source: %q, target: %q, flags: %#x, err: %v", spec.Root.Path, spec.Root.Path, flags, err) - } - } - return rv, nil -} - -// mountInfo returns whether the path is a mount point and whether the mount -// that path belongs to is read-only. -func mountInfo(path string) (bool, bool, error) { - // Mounts are listed by their real paths. - realPath, err := filepath.EvalSymlinks(path) - if err != nil { - return false, false, err - } - f, err := os.Open("/proc/mounts") - if err != nil { - return false, false, err - } - scanner := bufio.NewScanner(f) - - var mountPoint string - var readonly bool - for scanner.Scan() { - line := scanner.Text() - parts := strings.Split(line, " ") - if len(parts) < 4 { - return false, false, fmt.Errorf("invalid /proc/mounts line format %q", line) - } - mp := parts[1] - opts := strings.Split(parts[3], ",") - - // Find the closest submount to the path. - if strings.Contains(realPath, mp) && len(mp) > len(mountPoint) { - mountPoint = mp - readonly = specutils.ContainsStr(opts, "ro") - } - } - if err := scanner.Err(); err != nil { - return false, false, err - } - return mountPoint == realPath, readonly, nil -} - -// destroyFS unmounts mounts done by runsc under `spec.Root.Path`. This -// recovers the container rootfs into the original state. -func destroyFS(spec *specs.Spec) error { - for _, m := range spec.Mounts { - if m.Type != "bind" || !specutils.IsSupportedDevMount(m) { - continue - } - - // It's possible that 'm.Destination' follows symlinks inside the - // container. - dst, err := resolveSymlinks(spec.Root.Path, m.Destination) - if err != nil { - return err - } - - flags := syscall.MNT_DETACH - log.Infof("Unmounting dst: %q, flags: %#x", dst, flags) - // Do not return error if dst is not a mountpoint. - // Based on http://man7.org/linux/man-pages/man2/umount.2.html - // For kernel version 2.6+ and MNT_DETACH flag, EINVAL means - // the dst is not a mount point. - if err := syscall.Unmount(dst, flags); err != nil && - !os.IsNotExist(err) && err != syscall.EINVAL { - return err - } - } - return nil -} - -// resolveSymlinks walks 'rel' having 'root' as the root directory. If there are -// symlinks, they are evaluated relative to 'root' to ensure the end result is -// the same as if the process was running inside the container. -func resolveSymlinks(root, rel string) (string, error) { - return resolveSymlinksImpl(root, root, rel, 255) -} - -func resolveSymlinksImpl(root, base, rel string, followCount uint) (string, error) { - if followCount == 0 { - return "", fmt.Errorf("too many symlinks to follow, path: %q", filepath.Join(base, rel)) - } - - rel = filepath.Clean(rel) - for _, name := range strings.Split(rel, string(filepath.Separator)) { - if name == "" { - continue - } - // Note that Join() resolves things like ".." and returns a clean path. - path := filepath.Join(base, name) - if !strings.HasPrefix(path, root) { - // One cannot '..' their way out of root. - path = root - continue - } - fi, err := os.Lstat(path) - if err != nil { - if !os.IsNotExist(err) { - return "", err - } - // Not found means there is no symlink to check. Just keep walking dirs. - base = path - continue - } - if fi.Mode()&os.ModeSymlink != 0 { - link, err := os.Readlink(path) - if err != nil { - return "", err - } - if filepath.IsAbs(link) { - base = root - } - base, err = resolveSymlinksImpl(root, base, link, followCount-1) - if err != nil { - return "", err - } - continue - } - base = path - } - return base, nil -} - -func optionsToFlags(opts []string) uint32 { - var rv uint32 - for _, opt := range opts { - if m, ok := optionsMap[opt]; ok { - if m.set { - rv |= m.val - } else { - rv ^= m.val - } - } else { - log.Warningf("Ignoring mount option %q", opt) - } - } - return rv -} diff --git a/runsc/container/fs_test.go b/runsc/container/fs_test.go deleted file mode 100644 index 87cdb078e..000000000 --- a/runsc/container/fs_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 container - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "testing" - - "gvisor.googlesource.com/gvisor/runsc/test/testutil" -) - -type dir struct { - rel string - link string -} - -func construct(root string, dirs []dir) error { - for _, d := range dirs { - p := path.Join(root, d.rel) - if d.link == "" { - if err := os.MkdirAll(p, 0755); err != nil { - return fmt.Errorf("error creating dir: %v", err) - } - } else { - if err := os.MkdirAll(path.Dir(p), 0755); err != nil { - return fmt.Errorf("error creating dir: %v", err) - } - if err := os.Symlink(d.link, p); err != nil { - return fmt.Errorf("error creating symlink: %v", err) - } - } - } - return nil -} - -func TestResolveSymlinks(t *testing.T) { - root, err := ioutil.TempDir(testutil.TmpDir(), "root") - if err != nil { - t.Fatal("ioutil.TempDir() failed:", err) - } - dirs := []dir{ - {"dir1/dir11/dir111/dir1111", ""}, // Just a boring dir - {"dir1/lnk12", "dir11"}, // Link to sibling - {"dir1/lnk13", "./dir11"}, // Link to sibling through self - {"dir1/lnk14", "../dir1/dir11"}, // Link to sibling through parent - {"dir1/dir15/lnk151", ".."}, // Link to parent - {"dir1/lnk16", "dir11/dir111"}, // Link to child - {"dir1/lnk17", "."}, // Link to self - {"dir1/lnk18", "lnk13"}, // Link to link - {"lnk2", "dir1/lnk13"}, // Link to link to link - {"dir3/dir21/lnk211", "../.."}, // Link to root relative - {"dir3/lnk22", "/"}, // Link to root absolute - {"dir3/lnk23", "/dir1"}, // Link to dir absolute - {"dir3/lnk24", "/dir1/lnk12"}, // Link to link absolute - {"lnk5", "../../.."}, // Link outside root - } - if err := construct(root, dirs); err != nil { - t.Fatal("construct failed:", err) - } - - tests := []struct { - name string - rel string - want string - compareHost bool - }{ - {name: "root", rel: "/", want: "/", compareHost: true}, - {name: "basic dir", rel: "/dir1/dir11/dir111", want: "/dir1/dir11/dir111", compareHost: true}, - {name: "dot 1", rel: "/dir1/dir11/./dir111", want: "/dir1/dir11/dir111", compareHost: true}, - {name: "dot 2", rel: "/dir1/././dir11/./././././dir111/.", want: "/dir1/dir11/dir111", compareHost: true}, - {name: "dotdot 1", rel: "/dir1/dir11/../dir15", want: "/dir1/dir15", compareHost: true}, - {name: "dotdot 2", rel: "/dir1/dir11/dir1111/../..", want: "/dir1", compareHost: true}, - - {name: "link sibling", rel: "/dir1/lnk12", want: "/dir1/dir11", compareHost: true}, - {name: "link sibling + dir", rel: "/dir1/lnk12/dir111", want: "/dir1/dir11/dir111", compareHost: true}, - {name: "link sibling through self", rel: "/dir1/lnk13", want: "/dir1/dir11", compareHost: true}, - {name: "link sibling through parent", rel: "/dir1/lnk14", want: "/dir1/dir11", compareHost: true}, - - {name: "link parent", rel: "/dir1/dir15/lnk151", want: "/dir1", compareHost: true}, - {name: "link parent + dir", rel: "/dir1/dir15/lnk151/dir11", want: "/dir1/dir11", compareHost: true}, - {name: "link child", rel: "/dir1/lnk16", want: "/dir1/dir11/dir111", compareHost: true}, - {name: "link child + dir", rel: "/dir1/lnk16/dir1111", want: "/dir1/dir11/dir111/dir1111", compareHost: true}, - {name: "link self", rel: "/dir1/lnk17", want: "/dir1", compareHost: true}, - {name: "link self + dir", rel: "/dir1/lnk17/dir11", want: "/dir1/dir11", compareHost: true}, - - {name: "link^2", rel: "/dir1/lnk18", want: "/dir1/dir11", compareHost: true}, - {name: "link^2 + dir", rel: "/dir1/lnk18/dir111", want: "/dir1/dir11/dir111", compareHost: true}, - {name: "link^3", rel: "/lnk2", want: "/dir1/dir11", compareHost: true}, - {name: "link^3 + dir", rel: "/lnk2/dir111", want: "/dir1/dir11/dir111", compareHost: true}, - - {name: "link abs", rel: "/dir3/lnk23", want: "/dir1"}, - {name: "link abs + dir", rel: "/dir3/lnk23/dir11", want: "/dir1/dir11"}, - {name: "link^2 abs", rel: "/dir3/lnk24", want: "/dir1/dir11"}, - {name: "link^2 abs + dir", rel: "/dir3/lnk24/dir111", want: "/dir1/dir11/dir111"}, - - {name: "root link rel", rel: "/dir3/dir21/lnk211", want: "/", compareHost: true}, - {name: "root link abs", rel: "/dir3/lnk22", want: "/"}, - {name: "root contain link", rel: "/lnk5/dir1", want: "/dir1"}, - {name: "root contain dotdot", rel: "/dir1/dir11/../../../../../../../..", want: "/"}, - - {name: "crazy", rel: "/dir3/dir21/lnk211/dir3/lnk22/dir1/dir11/../../lnk5/dir3/../dir3/lnk24/dir111/dir1111/..", want: "/dir1/dir11/dir111"}, - } - for _, tst := range tests { - t.Run(tst.name, func(t *testing.T) { - got, err := resolveSymlinks(root, tst.rel) - if err != nil { - t.Errorf("resolveSymlinks(root, %q) failed: %v", tst.rel, err) - } - want := path.Join(root, tst.want) - if got != want { - t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, got, want) - } - if tst.compareHost { - // Check that host got to the same end result. - host, err := filepath.EvalSymlinks(path.Join(root, tst.rel)) - if err != nil { - t.Errorf("path.EvalSymlinks(root, %q) failed: %v", tst.rel, err) - } - if host != got { - t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, host, got) - } - } - }) - } -} - -func TestResolveSymlinksLoop(t *testing.T) { - root, err := ioutil.TempDir(testutil.TmpDir(), "root") - if err != nil { - t.Fatal("ioutil.TempDir() failed:", err) - } - dirs := []dir{ - {"loop1", "loop2"}, - {"loop2", "loop1"}, - } - if err := construct(root, dirs); err != nil { - t.Fatal("construct failed:", err) - } - if _, err := resolveSymlinks(root, "loop1"); err == nil { - t.Errorf("resolveSymlinks() should have failed") - } -} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 2698e3f86..ae6375e13 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -75,7 +75,7 @@ type Sandbox struct { // New creates the sandbox process. The caller must call Destroy() on the // sandbox. -func New(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File, cg *cgroup.Cgroup) (*Sandbox, error) { +func New(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File, specFile *os.File, cg *cgroup.Cgroup) (*Sandbox, error) { s := &Sandbox{ID: id, Cgroup: cg} // The Cleanup object cleans up partially created sandboxes when an error // occurs. Any errors occurring during cleanup itself are ignored. @@ -86,17 +86,14 @@ func New(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocke defer c.Clean() // Create pipe to synchronize when sandbox process has been booted. - fds := make([]int, 2) - if err := syscall.Pipe(fds); err != nil { + clientSyncFile, sandboxSyncFile, err := os.Pipe() + if err != nil { return nil, fmt.Errorf("creating pipe for sandbox %q: %v", s.ID, err) } - clientSyncFile := os.NewFile(uintptr(fds[0]), "client sandbox sync") defer clientSyncFile.Close() - sandboxSyncFile := os.NewFile(uintptr(fds[1]), "sandbox sync") - // Create the sandbox process. - err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, userLog, ioFiles, sandboxSyncFile) + err = s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, userLog, ioFiles, specFile, sandboxSyncFile) // sandboxSyncFile has to be closed to be able to detect when the sandbox // process exits unexpectedly. sandboxSyncFile.Close() @@ -294,7 +291,7 @@ func (s *Sandbox) connError(err error) error { // createSandboxProcess starts the sandbox as a subprocess by running the "boot" // command, passing in the bundle dir. -func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File, startSyncFile *os.File) error { +func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File, mountsFile, startSyncFile *os.File) error { // nextFD is used to get unused FDs that we can pass to the sandbox. It // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 @@ -345,10 +342,14 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund cmd.Args = append(cmd.Args, "--controller-fd="+strconv.Itoa(nextFD)) nextFD++ - // Open the spec file to donate to the sandbox. - specFile, err := specutils.OpenCleanSpec(bundleDir) + defer mountsFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, mountsFile) + cmd.Args = append(cmd.Args, "--mounts-fd="+strconv.Itoa(nextFD)) + nextFD++ + + specFile, err := specutils.OpenSpec(bundleDir) if err != nil { - return fmt.Errorf("opening spec file: %v", err) + return err } defer specFile.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, specFile) diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 372799850..15476de6f 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -5,6 +5,7 @@ package(licenses = ["notice"]) go_library( name = "specutils", srcs = [ + "fs.go", "namespace.go", "specutils.go", ], diff --git a/runsc/specutils/fs.go b/runsc/specutils/fs.go new file mode 100644 index 000000000..b812a5fbd --- /dev/null +++ b/runsc/specutils/fs.go @@ -0,0 +1,139 @@ +// Copyright 2018 Google LLC +// +// 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 specutils + +import ( + "fmt" + "path" + "syscall" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.googlesource.com/gvisor/pkg/log" +) + +type mapping struct { + set bool + val uint32 +} + +// optionsMap maps mount propagation-related OCI filesystem options to mount(2) +// syscall flags. +var optionsMap = map[string]mapping{ + "acl": {set: true, val: syscall.MS_POSIXACL}, + "async": {set: false, val: syscall.MS_SYNCHRONOUS}, + "atime": {set: false, val: syscall.MS_NOATIME}, + "bind": {set: true, val: syscall.MS_BIND}, + "defaults": {set: true, val: 0}, + "dev": {set: false, val: syscall.MS_NODEV}, + "diratime": {set: false, val: syscall.MS_NODIRATIME}, + "dirsync": {set: true, val: syscall.MS_DIRSYNC}, + "exec": {set: false, val: syscall.MS_NOEXEC}, + "iversion": {set: true, val: syscall.MS_I_VERSION}, + "loud": {set: false, val: syscall.MS_SILENT}, + "mand": {set: true, val: syscall.MS_MANDLOCK}, + "noacl": {set: false, val: syscall.MS_POSIXACL}, + "noatime": {set: true, val: syscall.MS_NOATIME}, + "nodev": {set: true, val: syscall.MS_NODEV}, + "nodiratime": {set: true, val: syscall.MS_NODIRATIME}, + "noiversion": {set: false, val: syscall.MS_I_VERSION}, + "nomand": {set: false, val: syscall.MS_MANDLOCK}, + "norelatime": {set: false, val: syscall.MS_RELATIME}, + "nostrictatime": {set: false, val: syscall.MS_STRICTATIME}, + "nosuid": {set: true, val: syscall.MS_NOSUID}, + "rbind": {set: true, val: syscall.MS_BIND | syscall.MS_REC}, + "relatime": {set: true, val: syscall.MS_RELATIME}, + "remount": {set: true, val: syscall.MS_REMOUNT}, + "ro": {set: true, val: syscall.MS_RDONLY}, + "rw": {set: false, val: syscall.MS_RDONLY}, + "silent": {set: true, val: syscall.MS_SILENT}, + "strictatime": {set: true, val: syscall.MS_STRICTATIME}, + "suid": {set: false, val: syscall.MS_NOSUID}, + "sync": {set: true, val: syscall.MS_SYNCHRONOUS}, +} + +// propOptionsMap is similar to optionsMap, but it lists propagation options +// that cannot be used together with other flags. +var propOptionsMap = map[string]mapping{ + "private": {set: true, val: syscall.MS_PRIVATE}, + "rprivate": {set: true, val: syscall.MS_PRIVATE | syscall.MS_REC}, + "slave": {set: true, val: syscall.MS_SLAVE}, + "rslave": {set: true, val: syscall.MS_SLAVE | syscall.MS_REC}, + "unbindable": {set: true, val: syscall.MS_UNBINDABLE}, + "runbindable": {set: true, val: syscall.MS_UNBINDABLE | syscall.MS_REC}, +} + +// invalidOptions list options not allowed. +// - shared: sandbox must be isolated from the host. Propagating mount changes +// from the sandbox to the host breaks the isolation. +// - noexec: not yet supported. Don't ignore it since it could break +// in-sandbox security. +var invalidOptions = []string{"shared", "rshared", "noexec"} + +// OptionsToFlags converts mount options to syscall flags. +func OptionsToFlags(opts []string) uint32 { + return optionsToFlags(opts, optionsMap) +} + +// PropOptionsToFlags converts propagation mount options to syscall flags. +// Propagation options cannot be set other with other options and must be +// handled separatedly. +func PropOptionsToFlags(opts []string) uint32 { + return optionsToFlags(opts, propOptionsMap) +} + +func optionsToFlags(opts []string, source map[string]mapping) uint32 { + var rv uint32 + for _, opt := range opts { + if m, ok := source[opt]; ok { + if m.set { + rv |= m.val + } else { + rv ^= m.val + } + } + } + return rv +} + +// ValidateMount validates that spec mounts are correct. +func validateMount(mnt *specs.Mount) error { + if !path.IsAbs(mnt.Destination) { + return fmt.Errorf("Mount.Destination must be an absolute path: %v", mnt) + } + + if mnt.Type == "bind" { + for _, o := range mnt.Options { + if ContainsStr(invalidOptions, o) { + return fmt.Errorf("mount option %q is not supported: %v", o, mnt) + } + _, ok1 := optionsMap[o] + _, ok2 := propOptionsMap[o] + if !ok1 && !ok2 { + log.Warningf("Ignoring unknown mount option %q", o) + } + } + } + return nil +} + +// ValidateRootfsPropagation validates that rootfs propagation options are +// correct. +func validateRootfsPropagation(opt string) error { + flags := PropOptionsToFlags([]string{opt}) + if flags&(syscall.MS_SLAVE|syscall.MS_PRIVATE) == 0 { + return fmt.Errorf("root mount propagation option must specify private or slave: %q", opt) + } + return nil +} diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go index 73fab13e1..35da789f4 100644 --- a/runsc/specutils/namespace.go +++ b/runsc/specutils/namespace.go @@ -105,9 +105,9 @@ func FilterNS(filter []specs.LinuxNamespaceType, s *specs.Spec) []specs.LinuxNam return out } -// SetNS sets the namespace of the given type. It must be called with +// setNS sets the namespace of the given type. It must be called with // OSThreadLocked. -func SetNS(fd, nsType uintptr) error { +func setNS(fd, nsType uintptr) error { if _, _, err := syscall.RawSyscall(unix.SYS_SETNS, fd, nsType, 0); err != 0 { return err } @@ -119,30 +119,30 @@ func SetNS(fd, nsType uintptr) error { // // Preconditions: Must be called with os thread locked. func ApplyNS(ns specs.LinuxNamespace) (func(), error) { - log.Infof("applying namespace %v at path %q", ns.Type, ns.Path) + log.Infof("Applying namespace %v at path %q", ns.Type, ns.Path) newNS, err := os.Open(ns.Path) if err != nil { return nil, fmt.Errorf("error opening %q: %v", ns.Path, err) } defer newNS.Close() - // Store current netns to restore back after child is started. + // Store current namespace to restore back. curPath := nsPath(ns.Type) oldNS, err := os.Open(curPath) if err != nil { return nil, fmt.Errorf("error opening %q: %v", curPath, err) } - // Set netns to the one requested and setup function to restore it back. + // Set namespace to the one requested and setup function to restore it back. flag := nsCloneFlag(ns.Type) - if err := SetNS(newNS.Fd(), flag); err != nil { + if err := setNS(newNS.Fd(), flag); err != nil { oldNS.Close() return nil, fmt.Errorf("error setting namespace of type %v and path %q: %v", ns.Type, ns.Path, err) } return func() { - log.Infof("restoring namespace %v", ns.Type) + log.Infof("Restoring namespace %v", ns.Type) defer oldNS.Close() - if err := SetNS(oldNS.Fd(), flag); err != nil { + if err := setNS(oldNS.Fd(), flag); err != nil { panic(fmt.Sprintf("error restoring namespace: of type %v: %v", ns.Type, err)) } }, nil diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 4e7893ab4..cbf099c64 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -92,9 +92,14 @@ func ValidateSpec(spec *specs.Spec) error { log.Warningf("Seccomp spec is being ignored") } - for i, m := range spec.Mounts { - if !path.IsAbs(m.Destination) { - return fmt.Errorf("Spec.Mounts[%d] Mount.Destination must be an absolute path: %v", i, m) + if spec.Linux != nil && spec.Linux.RootfsPropagation != "" { + if err := validateRootfsPropagation(spec.Linux.RootfsPropagation); err != nil { + return err + } + } + for _, m := range spec.Mounts { + if err := validateMount(&m); err != nil { + return err } } @@ -129,15 +134,19 @@ func absPath(base, rel string) string { return filepath.Join(base, rel) } +// OpenSpec opens an OCI runtime spec from the given bundle directory. +func OpenSpec(bundleDir string) (*os.File, error) { + // The spec file must be named "config.json" inside the bundle directory. + return os.Open(filepath.Join(bundleDir, "config.json")) +} + // ReadSpec reads an OCI runtime spec from the given bundle directory. // ReadSpec also normalizes all potential relative paths into absolute // path, e.g. spec.Root.Path, mount.Source. func ReadSpec(bundleDir string) (*specs.Spec, error) { - // The spec file must be in "config.json" inside the bundle directory. - specPath := filepath.Join(bundleDir, "config.json") - specFile, err := os.Open(specPath) + specFile, err := OpenSpec(bundleDir) if err != nil { - return nil, fmt.Errorf("error opening spec file %q: %v", specPath, err) + return nil, fmt.Errorf("error opening spec file %q: %v", specFile.Name(), err) } defer specFile.Close() return ReadSpecFromFile(bundleDir, specFile) @@ -171,27 +180,17 @@ func ReadSpecFromFile(bundleDir string, specFile *os.File) (*specs.Spec, error) return &spec, nil } -// OpenCleanSpec opens spec file that has destination mount paths resolved to -// their absolute location. -func OpenCleanSpec(bundleDir string) (*os.File, error) { - f, err := os.Open(filepath.Join(bundleDir, "config.clean.json")) +// ReadMounts reads mount list from a file. +func ReadMounts(f *os.File) ([]specs.Mount, error) { + bytes, err := ioutil.ReadAll(f) if err != nil { - return nil, err + return nil, fmt.Errorf("error reading mounts: %v", err) } - if _, err := f.Seek(0, os.SEEK_SET); err != nil { - f.Close() - return nil, fmt.Errorf("error seeking to beginning of file %q: %v", f.Name(), err) - } - return f, nil -} - -// WriteCleanSpec writes a spec file that has destination mount paths resolved. -func WriteCleanSpec(bundleDir string, spec *specs.Spec) error { - bytes, err := json.Marshal(spec) - if err != nil { - return err + var mounts []specs.Mount + if err := json.Unmarshal(bytes, &mounts); err != nil { + return nil, fmt.Errorf("error unmarshaling mounts: %v\n %s", err, string(bytes)) } - return ioutil.WriteFile(filepath.Join(bundleDir, "config.clean.json"), bytes, 0755) + return mounts, nil } // Capabilities takes in spec and returns a TaskCapabilities corresponding to @@ -407,8 +406,7 @@ func Mount(src, dst, typ string, flags uint32) error { // source (file or directory). var isDir bool if typ == "proc" { - // Special case, as there is no source directory for proc - // mounts. + // Special case, as there is no source directory for proc mounts. isDir = true } else if fi, err := os.Stat(src); err != nil { return fmt.Errorf("Stat(%q) failed: %v", src, err) diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go index b61f1ca62..02af6e6ad 100644 --- a/runsc/specutils/specutils_test.go +++ b/runsc/specutils/specutils_test.go @@ -219,6 +219,37 @@ func TestSpecInvalid(t *testing.T) { }, error: "must be an absolute path", }, + { + name: "invalid mount option", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + Mounts: []specs.Mount{ + { + Source: "/src", + Destination: "/dst", + Type: "bind", + Options: []string{"shared"}, + }, + }, + }, + error: "is not supported", + }, + { + name: "invalid rootfs propagation", + spec: specs.Spec{ + Root: &specs.Root{Path: "/"}, + Process: &specs.Process{ + Args: []string{"/bin/true"}, + }, + Linux: &specs.Linux{ + RootfsPropagation: "foo", + }, + }, + error: "root mount propagation option must specify private or slave", + }, } { err := ValidateSpec(&test.spec) if len(test.error) == 0 { -- cgit v1.2.3 From c7877b0a14778af9165eb2b841513b6f7dfdcbee Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Wed, 20 Mar 2019 10:35:13 -0700 Subject: Fail in case mount option is unknown PiperOrigin-RevId: 239425816 Change-Id: I3b1479c61b4222c3931a416c4efc909157044330 --- runsc/specutils/fs.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'runsc/specutils') diff --git a/runsc/specutils/fs.go b/runsc/specutils/fs.go index b812a5fbd..aa17d4eb9 100644 --- a/runsc/specutils/fs.go +++ b/runsc/specutils/fs.go @@ -20,7 +20,6 @@ import ( "syscall" specs "github.com/opencontainers/runtime-spec/specs-go" - "gvisor.googlesource.com/gvisor/pkg/log" ) type mapping struct { @@ -121,7 +120,7 @@ func validateMount(mnt *specs.Mount) error { _, ok1 := optionsMap[o] _, ok2 := propOptionsMap[o] if !ok1 && !ok2 { - log.Warningf("Ignoring unknown mount option %q", o) + return fmt.Errorf("unknown mount option %q", o) } } } -- cgit v1.2.3 From 7543e9ec2043af7d071373aeec04b92a98051087 Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Mon, 1 Apr 2019 16:17:40 -0700 Subject: Add release hook and version flag PiperOrigin-RevId: 241421671 Change-Id: Ic0cebfe3efd458dc42c49f7f812c13318705199a --- runsc/BUILD | 6 +++-- runsc/main.go | 15 +++++++++---- runsc/specutils/specutils.go | 3 +++ runsc/version.go | 18 +++++++++++++++ tools/tag_release.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++ tools/workspace_status.sh | 2 +- 6 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 runsc/version.go create mode 100755 tools/tag_release.sh (limited to 'runsc/specutils') diff --git a/runsc/BUILD b/runsc/BUILD index e390b7bae..eb7503502 100644 --- a/runsc/BUILD +++ b/runsc/BUILD @@ -6,12 +6,13 @@ go_binary( name = "runsc", srcs = [ "main.go", + "version.go", ], pure = "on", visibility = [ "//visibility:public", ], - x_defs = {"main.gitRevision": "{GIT_REVISION}"}, + x_defs = {"main.version": "{VERSION}"}, deps = [ "//pkg/log", "//runsc/boot", @@ -36,12 +37,13 @@ go_binary( name = "runsc-race", srcs = [ "main.go", + "version.go", ], static = "on", visibility = [ "//visibility:public", ], - x_defs = {"main.gitRevision": "{GIT_REVISION}"}, + x_defs = {"main.version": "{VERSION}"}, deps = [ "//pkg/log", "//runsc/boot", diff --git a/runsc/main.go b/runsc/main.go index 4b3f55ad1..bbf08228c 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -18,6 +18,7 @@ package main import ( "context" + "fmt" "io" "os" "path/filepath" @@ -40,6 +41,7 @@ var ( logFilename = flag.String("log", "", "file path where internal debug information is written, default is stdout") logFormat = flag.String("log-format", "text", "log format: text (default), json, or json-k8s") debug = flag.Bool("debug", false, "enable debug logging") + showVersion = flag.Bool("version", false, "show version and exit") // These flags are unique to runsc, and are used to configure parts of the // system that are not covered by the runtime spec. @@ -69,9 +71,6 @@ var ( testOnlyAllowRunAsCurrentUserWithoutChroot = flag.Bool("TESTONLY-unsafe-nonroot", false, "TEST ONLY; do not ever use! This skips many security measures that isolate the host from the sandbox.") ) -// gitRevision is set during linking. -var gitRevision = "" - func main() { // Help and flags commands are generated automatically. subcommands.Register(subcommands.HelpCommand(), "") @@ -107,6 +106,14 @@ func main() { // All subcommands must be registered before flag parsing. flag.Parse() + // 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, "spec: %s\n", specutils.Version) + os.Exit(0) + } + platformType, err := boot.MakePlatformType(*platform) if err != nil { cmd.Fatalf("%v", err) @@ -215,7 +222,7 @@ func main() { log.Infof("***************************") log.Infof("Args: %s", os.Args) - log.Infof("Git Revision: %s", gitRevision) + log.Infof("Version %s", version) log.Infof("PID: %d", os.Getpid()) log.Infof("UID: %d, GID: %d", os.Getuid(), os.Getgid()) log.Infof("Configuration:") diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index cbf099c64..af8d34535 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -38,6 +38,9 @@ import ( // changed in tests that aren't linked in the same binary. var ExePath = "/proc/self/exe" +// Version is the supported spec version. +var Version = specs.Version + // LogSpec logs the spec in a human-friendly way. func LogSpec(spec *specs.Spec) { log.Debugf("Spec: %+v", spec) diff --git a/runsc/version.go b/runsc/version.go new file mode 100644 index 000000000..4894f2de6 --- /dev/null +++ b/runsc/version.go @@ -0,0 +1,18 @@ +// Copyright 2019 Google LLC +// +// 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 main + +// version is set during linking. +var version = "" diff --git a/tools/tag_release.sh b/tools/tag_release.sh new file mode 100755 index 000000000..6906a952f --- /dev/null +++ b/tools/tag_release.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# Copyright 2019 Google LLC +# +# 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. + +# This script will optionally map a PiperOrigin-RevId to a given commit, +# validate a provided release name, create a tag and push it. It must be +# run manually when a release is created. + +set -euxo pipefail + +# Check arguments. +if [ "$#" -ne 2 ]; then + echo "usage: $0 " + exit 1 +fi + +commit=$1 +release=$2 + +# Is the passed identifier a sha commit? +if ! git show "${commit}" &> /dev/null; then + # Extract the commit given a piper ID. + commit=$(git log|grep -E "(^commit |^ PiperOrigin-RevId:)" |grep -B1 "RevId: ${commit}"| head -n1|cut -d" " -f2) +fi +if ! git show "${commit}" &> /dev/null; then + echo "unknown commit: ${commit}" + exit 1 +fi + +# Is the release name sane? Must be a date with patch/rc. +if ! [[ "${release}" =~ ^20[0-9]{6}\.[0-9]+$ ]]; then + expected=$(date +%Y%m%d.0) # Use today's date. + echo "unexpected release format: ${release}" + echo " ... expected like ${expected}" + exit 1 +fi + +# Tag the given commit. +tag="release-${release}" +(git tag "${tag}" "${commit}" && git push origin tag "${tag}") || \ + (git tag -d "${tag}" && false) diff --git a/tools/workspace_status.sh b/tools/workspace_status.sh index 7d44dad37..a0e646e45 100755 --- a/tools/workspace_status.sh +++ b/tools/workspace_status.sh @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -echo GIT_REVISION $(git describe --always --abbrev=40 --dirty) +echo VERSION $(git describe --always --tags --abbrev=12 --dirty) -- cgit v1.2.3 From 88409e983c463b6d9c8085e7fdbe7ff45b3c5184 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Thu, 4 Apr 2019 17:42:51 -0700 Subject: gvisor: Add support for the MS_NOEXEC mount option https://github.com/google/gvisor/issues/145 PiperOrigin-RevId: 242044115 Change-Id: I8f140fe05e32ecd438b6be218e224e4b7fe05878 --- pkg/sentry/fs/context.go | 5 +++++ pkg/sentry/fs/filesystems.go | 4 ++++ pkg/sentry/fs/proc/mounts.go | 3 +++ pkg/sentry/syscalls/linux/sys_mount.go | 5 ++++- runsc/boot/fs.go | 2 ++ runsc/specutils/fs.go | 5 ++--- test/syscalls/linux/BUILD | 1 + test/syscalls/linux/mount.cc | 18 ++++++++++++++++++ 8 files changed, 39 insertions(+), 4 deletions(-) (limited to 'runsc/specutils') diff --git a/pkg/sentry/fs/context.go b/pkg/sentry/fs/context.go index 1775d3486..c0e6075e4 100644 --- a/pkg/sentry/fs/context.go +++ b/pkg/sentry/fs/context.go @@ -46,6 +46,11 @@ func ContextCanAccessFile(ctx context.Context, inode *Inode, reqPerms PermMask) p = uattr.Perms.Group } + // Do not allow programs to be executed if MS_NOEXEC is set. + if IsFile(inode.StableAttr) && reqPerms.Execute && inode.MountSource.Flags.NoExec { + return false + } + // Are permissions satisfied without capability checks? if p.SupersetOf(reqPerms) { return true diff --git a/pkg/sentry/fs/filesystems.go b/pkg/sentry/fs/filesystems.go index aa664b973..a6b27c402 100644 --- a/pkg/sentry/fs/filesystems.go +++ b/pkg/sentry/fs/filesystems.go @@ -140,6 +140,10 @@ type MountSourceFlags struct { // cache, even when the platform supports direct mapped I/O. This // doesn't correspond to any Linux mount options. ForcePageCache bool + + // NoExec corresponds to mount(2)'s "MS_NOEXEC" and indicates that + // binaries from this file system can't be executed. + NoExec bool } // GenericMountSourceOptions splits a string containing comma separated tokens of the diff --git a/pkg/sentry/fs/proc/mounts.go b/pkg/sentry/fs/proc/mounts.go index 7111e5c0f..1e62af8c6 100644 --- a/pkg/sentry/fs/proc/mounts.go +++ b/pkg/sentry/fs/proc/mounts.go @@ -129,6 +129,9 @@ func (mif *mountInfoFile) ReadSeqFileData(ctx context.Context, handle seqfile.Se if m.Flags.NoAtime { opts += ",noatime" } + if m.Flags.NoExec { + opts += ",noexec" + } fmt.Fprintf(&buf, "%s ", opts) // (7) Optional fields: zero or more fields of the form "tag[:value]". diff --git a/pkg/sentry/syscalls/linux/sys_mount.go b/pkg/sentry/syscalls/linux/sys_mount.go index 6b8d75d24..e110a553f 100644 --- a/pkg/sentry/syscalls/linux/sys_mount.go +++ b/pkg/sentry/syscalls/linux/sys_mount.go @@ -75,7 +75,7 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall // Silently allow MS_NOSUID, since we don't implement set-id bits // anyway. - const unsupportedFlags = linux.MS_NODEV | linux.MS_NOEXEC | + const unsupportedFlags = linux.MS_NODEV | linux.MS_NODIRATIME | linux.MS_STRICTATIME // Linux just allows passing any flags to mount(2) - it won't fail when @@ -100,6 +100,9 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall if flags&linux.MS_RDONLY == linux.MS_RDONLY { superFlags.ReadOnly = true } + if flags&linux.MS_NOEXEC == linux.MS_NOEXEC { + superFlags.NoExec = true + } rootInode, err := rsys.Mount(t, sourcePath, superFlags, data, nil) if err != nil { diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 25e23c09b..8dfb6dce6 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -482,6 +482,8 @@ func mountFlags(opts []string) fs.MountSourceFlags { mf.ReadOnly = true case "noatime": mf.NoAtime = true + case "noexec": + mf.NoExec = true default: log.Warningf("ignoring unknown mount option %q", o) } diff --git a/runsc/specutils/fs.go b/runsc/specutils/fs.go index aa17d4eb9..98c3b19c0 100644 --- a/runsc/specutils/fs.go +++ b/runsc/specutils/fs.go @@ -39,6 +39,7 @@ var optionsMap = map[string]mapping{ "diratime": {set: false, val: syscall.MS_NODIRATIME}, "dirsync": {set: true, val: syscall.MS_DIRSYNC}, "exec": {set: false, val: syscall.MS_NOEXEC}, + "noexec": {set: true, val: syscall.MS_NOEXEC}, "iversion": {set: true, val: syscall.MS_I_VERSION}, "loud": {set: false, val: syscall.MS_SILENT}, "mand": {set: true, val: syscall.MS_MANDLOCK}, @@ -76,9 +77,7 @@ var propOptionsMap = map[string]mapping{ // invalidOptions list options not allowed. // - shared: sandbox must be isolated from the host. Propagating mount changes // from the sandbox to the host breaks the isolation. -// - noexec: not yet supported. Don't ignore it since it could break -// in-sandbox security. -var invalidOptions = []string{"shared", "rshared", "noexec"} +var invalidOptions = []string{"shared", "rshared"} // OptionsToFlags converts mount options to syscall flags. func OptionsToFlags(opts []string) uint32 { diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 1e386193b..38faba267 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1080,6 +1080,7 @@ cc_binary( "//test/util:file_descriptor", "//test/util:fs_util", "//test/util:mount_util", + "//test/util:multiprocess_util", "//test/util:posix_error", "//test/util:temp_path", "//test/util:test_main", diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc index 6bb4287a3..201b83e87 100644 --- a/test/syscalls/linux/mount.cc +++ b/test/syscalls/linux/mount.cc @@ -31,6 +31,7 @@ #include "test/util/file_descriptor.h" #include "test/util/fs_util.h" #include "test/util/mount_util.h" +#include "test/util/multiprocess_util.h" #include "test/util/posix_error.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" @@ -277,6 +278,23 @@ TEST(MountTest, MountNoAtime) { EXPECT_EQ(before, after); } +TEST(MountTest, MountNoExec) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto const mount = ASSERT_NO_ERRNO_AND_VALUE( + Mount("", dir.path(), "tmpfs", MS_NOEXEC, "mode=0777", 0)); + + std::string const contents = "No no no, don't follow the instructions!"; + auto const file = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateFileWith(dir.path(), contents, 0777)); + + int execve_errno; + ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec(file.path(), {}, {}, nullptr, &execve_errno)); + EXPECT_EQ(execve_errno, EACCES); +} + TEST(MountTest, RenameRemoveMountPoint) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); -- cgit v1.2.3 From 43dff57b878edb5502daf486cbc13b058780dd56 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 26 Apr 2019 16:50:35 -0700 Subject: Make raw sockets a toggleable feature disabled by default. PiperOrigin-RevId: 245511019 Change-Id: Ia9562a301b46458988a6a1f0bbd5f07cbfcb0615 --- pkg/syserr/netstack.go | 2 ++ pkg/tcpip/stack/stack.go | 12 ++++++++++++ pkg/tcpip/tcpip.go | 1 + pkg/tcpip/transport/tcp/endpoint_state.go | 1 + runsc/boot/config.go | 6 ++++++ runsc/boot/loader.go | 7 +++++-- runsc/cmd/exec.go | 18 +++++++++++++++--- runsc/main.go | 2 ++ runsc/specutils/specutils.go | 22 ++++++++++++++++------ runsc/test/integration/BUILD | 5 ++++- runsc/test/integration/exec_test.go | 26 +++++++++++++++++++++++--- runsc/test/testutil/docker.go | 21 +++++++++++++++++---- 12 files changed, 104 insertions(+), 19 deletions(-) (limited to 'runsc/specutils') diff --git a/pkg/syserr/netstack.go b/pkg/syserr/netstack.go index c5a628c7d..1a23919ef 100644 --- a/pkg/syserr/netstack.go +++ b/pkg/syserr/netstack.go @@ -45,6 +45,7 @@ var ( ErrNoSuchFile = New(tcpip.ErrNoSuchFile.String(), linux.ENOENT) ErrInvalidOptionValue = New(tcpip.ErrInvalidOptionValue.String(), linux.EINVAL) ErrBroadcastDisabled = New(tcpip.ErrBroadcastDisabled.String(), linux.EACCES) + ErrNotPermittedNet = New(tcpip.ErrNotPermitted.String(), linux.EPERM) ) var netstackErrorTranslations = map[*tcpip.Error]*Error{ @@ -84,6 +85,7 @@ var netstackErrorTranslations = map[*tcpip.Error]*Error{ tcpip.ErrMessageTooLong: ErrMessageTooLong, tcpip.ErrNoBufferSpace: ErrNoBufferSpace, tcpip.ErrBroadcastDisabled: ErrBroadcastDisabled, + tcpip.ErrNotPermitted: ErrNotPermittedNet, } // TranslateNetstackError converts an error from the tcpip package to a sentry diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index a74c0a7a0..8f7b6f781 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -291,6 +291,10 @@ type Stack struct { linkAddrCache *linkAddrCache + // raw indicates whether raw sockets may be created. It is set during + // Stack creation and is immutable. + raw bool + mu sync.RWMutex nics map[tcpip.NICID]*NIC forwarding bool @@ -327,6 +331,9 @@ type Options struct { // should be handled by the stack internally (true) or outside the // stack (false). HandleLocal bool + + // Raw indicates whether raw sockets may be created. + Raw bool } // New allocates a new networking stack with only the requested networking and @@ -352,6 +359,7 @@ func New(network []string, transport []string, opts Options) *Stack { clock: clock, stats: opts.Stats.FillIn(), handleLocal: opts.HandleLocal, + raw: opts.Raw, } // Add specified network protocols. @@ -512,6 +520,10 @@ func (s *Stack) NewEndpoint(transport tcpip.TransportProtocolNumber, network tcp // protocol. Raw endpoints receive all traffic for a given protocol regardless // of address. func (s *Stack) NewRawEndpoint(transport tcpip.TransportProtocolNumber, network tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) { + if !s.raw { + return nil, tcpip.ErrNotPermitted + } + t, ok := s.transportProtocols[transport] if !ok { return nil, tcpip.ErrUnknownProtocol diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index e898dcbca..80cd6b4e5 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -102,6 +102,7 @@ var ( ErrMessageTooLong = &Error{msg: "message too long"} ErrNoBufferSpace = &Error{msg: "no buffer space available"} ErrBroadcastDisabled = &Error{msg: "broadcast socket option disabled"} + ErrNotPermitted = &Error{msg: "operation not permitted"} ) // Errors related to Subnet diff --git a/pkg/tcpip/transport/tcp/endpoint_state.go b/pkg/tcpip/transport/tcp/endpoint_state.go index a42e09b8c..7f9dabb4d 100644 --- a/pkg/tcpip/transport/tcp/endpoint_state.go +++ b/pkg/tcpip/transport/tcp/endpoint_state.go @@ -341,6 +341,7 @@ func loadError(s string) *tcpip.Error { tcpip.ErrMessageTooLong, tcpip.ErrNoBufferSpace, tcpip.ErrBroadcastDisabled, + tcpip.ErrNotPermitted, } messageToError = make(map[string]*tcpip.Error) diff --git a/runsc/boot/config.go b/runsc/boot/config.go index 2523077fd..ba47effc1 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -175,6 +175,11 @@ type Config struct { // Network indicates what type of network to use. Network NetworkType + // EnableRaw indicates whether raw sockets should be enabled. Raw + // sockets are disabled by stripping CAP_NET_RAW from the list of + // capabilities. + EnableRaw bool + // GSO indicates that generic segmentation offload is enabled. GSO bool @@ -235,6 +240,7 @@ func (c *Config) ToFlags() []string { "--watchdog-action=" + c.WatchdogAction.String(), "--panic-signal=" + strconv.Itoa(c.PanicSignal), "--profile=" + strconv.FormatBool(c.ProfileEnable), + "--net-raw=" + strconv.FormatBool(c.EnableRaw), } if c.TestOnlyAllowRunAsCurrentUserWithoutChroot { // Only include if set since it is never to be used by users. diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 88a834aa5..48ecb2626 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -227,7 +227,7 @@ func New(args Args) (*Loader, error) { } // Create capabilities. - caps, err := specutils.Capabilities(args.Spec.Process.Capabilities) + caps, err := specutils.Capabilities(args.Conf.EnableRaw, args.Spec.Process.Capabilities) if err != nil { return nil, fmt.Errorf("converting capabilities: %v", err) } @@ -554,7 +554,7 @@ func (l *Loader) createContainer(cid string) error { // this method returns. func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config, cid string, files []*os.File) error { // Create capabilities. - caps, err := specutils.Capabilities(spec.Process.Capabilities) + caps, err := specutils.Capabilities(conf.EnableRaw, spec.Process.Capabilities) if err != nil { return fmt.Errorf("creating capabilities: %v", err) } @@ -800,6 +800,9 @@ func newEmptyNetworkStack(conf *Config, clock tcpip.Clock) (inet.Stack, error) { Clock: clock, Stats: epsocket.Metrics, HandleLocal: true, + // Enable raw sockets for users with sufficient + // privileges. + Raw: true, })} if err := s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, tcp.SACKEnabled(true)); err != nil { return nil, fmt.Errorf("failed to enable SACK: %v", err) diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index 9e058ad97..718d01067 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -132,7 +132,11 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } } if e.Capabilities == nil { - e.Capabilities, err = specutils.Capabilities(c.Spec.Process.Capabilities) + // enableRaw is set to true to prevent the filtering out of + // CAP_NET_RAW. This is the opposite of Create() because exec + // requires the capability to be set explicitly, while 'docker + // run' sets it by default. + e.Capabilities, err = specutils.Capabilities(true /* enableRaw */, c.Spec.Process.Capabilities) if err != nil { Fatalf("creating capabilities: %v", err) } @@ -351,7 +355,11 @@ func argsFromProcess(p *specs.Process) (*control.ExecArgs, error) { var caps *auth.TaskCapabilities if p.Capabilities != nil { var err error - caps, err = specutils.Capabilities(p.Capabilities) + // enableRaw is set to true to prevent the filtering out of + // CAP_NET_RAW. This is the opposite of Create() because exec + // requires the capability to be set explicitly, while 'docker + // run' sets it by default. + caps, err = specutils.Capabilities(true /* enableRaw */, p.Capabilities) if err != nil { return nil, fmt.Errorf("error creating capabilities: %v", err) } @@ -413,7 +421,11 @@ func capabilities(cs []string) (*auth.TaskCapabilities, error) { specCaps.Inheritable = append(specCaps.Inheritable, cap) specCaps.Permitted = append(specCaps.Permitted, cap) } - return specutils.Capabilities(&specCaps) + // enableRaw is set to true to prevent the filtering out of + // CAP_NET_RAW. This is the opposite of Create() because exec requires + // the capability to be set explicitly, while 'docker run' sets it by + // default. + return specutils.Capabilities(true /* enableRaw */, &specCaps) } // stringSlice allows a flag to be used multiple times, where each occurrence diff --git a/runsc/main.go b/runsc/main.go index 74253a844..b35726a74 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -68,6 +68,7 @@ var ( watchdogAction = flag.String("watchdog-action", "log", "sets what action the watchdog takes when triggered: log (default), panic.") panicSignal = flag.Int("panic-signal", -1, "register signal handling that panics. Usually set to SIGUSR2(12) to troubleshoot hangs. -1 disables it.") profile = flag.Bool("profile", false, "prepares the sandbox to use Golang profiler. Note that enabling profiler loosens the seccomp protection added to the sandbox (DO NOT USE IN PRODUCTION).") + netRaw = flag.Bool("net-raw", false, "enable raw sockets. When false, raw sockets are disabled by removing CAP_NET_RAW from containers (`runsc exec` will still be able to utilize raw sockets). Raw sockets allow malicious containers to craft packets and potentially attack the network.") testOnlyAllowRunAsCurrentUserWithoutChroot = flag.Bool("TESTONLY-unsafe-nonroot", false, "TEST ONLY; do not ever use! This skips many security measures that isolate the host from the sandbox.") ) @@ -159,6 +160,7 @@ func main() { WatchdogAction: wa, PanicSignal: *panicSignal, ProfileEnable: *profile, + EnableRaw: *netRaw, TestOnlyAllowRunAsCurrentUserWithoutChroot: *testOnlyAllowRunAsCurrentUserWithoutChroot, } if len(*straceSyscalls) != 0 { diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index af8d34535..32f81b8d4 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -198,20 +198,26 @@ func ReadMounts(f *os.File) ([]specs.Mount, error) { // Capabilities takes in spec and returns a TaskCapabilities corresponding to // the spec. -func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) { +func Capabilities(enableRaw bool, specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) { + // Strip CAP_NET_RAW from all capability sets if necessary. + skipSet := map[linux.Capability]struct{}{} + if !enableRaw { + skipSet[linux.CAP_NET_RAW] = struct{}{} + } + var caps auth.TaskCapabilities if specCaps != nil { var err error - if caps.BoundingCaps, err = capsFromNames(specCaps.Bounding); err != nil { + if caps.BoundingCaps, err = capsFromNames(specCaps.Bounding, skipSet); err != nil { return nil, err } - if caps.EffectiveCaps, err = capsFromNames(specCaps.Effective); err != nil { + if caps.EffectiveCaps, err = capsFromNames(specCaps.Effective, skipSet); err != nil { return nil, err } - if caps.InheritableCaps, err = capsFromNames(specCaps.Inheritable); err != nil { + if caps.InheritableCaps, err = capsFromNames(specCaps.Inheritable, skipSet); err != nil { return nil, err } - if caps.PermittedCaps, err = capsFromNames(specCaps.Permitted); err != nil { + if caps.PermittedCaps, err = capsFromNames(specCaps.Permitted, skipSet); err != nil { return nil, err } // TODO: Support ambient capabilities. @@ -275,13 +281,17 @@ var capFromName = map[string]linux.Capability{ "CAP_AUDIT_READ": linux.CAP_AUDIT_READ, } -func capsFromNames(names []string) (auth.CapabilitySet, error) { +func capsFromNames(names []string, skipSet map[linux.Capability]struct{}) (auth.CapabilitySet, error) { var caps []linux.Capability for _, n := range names { c, ok := capFromName[n] if !ok { return 0, fmt.Errorf("unknown capability %q", n) } + // Should we skip this capabilty? + if _, ok := skipSet[c]; ok { + continue + } caps = append(caps, c) } return auth.CapabilitySetOfMany(caps), nil diff --git a/runsc/test/integration/BUILD b/runsc/test/integration/BUILD index 779d30ec9..0c4e4fa80 100644 --- a/runsc/test/integration/BUILD +++ b/runsc/test/integration/BUILD @@ -15,7 +15,10 @@ go_test( "manual", "local", ], - deps = ["//runsc/test/testutil"], + deps = [ + "//pkg/abi/linux", + "//runsc/test/testutil", + ], ) go_library( diff --git a/runsc/test/integration/exec_test.go b/runsc/test/integration/exec_test.go index fac8337f4..d87957e2d 100644 --- a/runsc/test/integration/exec_test.go +++ b/runsc/test/integration/exec_test.go @@ -27,10 +27,13 @@ package integration import ( + "fmt" + "strconv" "syscall" "testing" "time" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/runsc/test/testutil" ) @@ -46,11 +49,28 @@ func TestExecCapabilities(t *testing.T) { } defer d.CleanUp() - want, err := d.WaitForOutput("CapEff:\t[0-9a-f]+\n", 5*time.Second) + matches, err := d.WaitForOutputSubmatch("CapEff:\t([0-9a-f]+)\n", 5*time.Second) if err != nil { - t.Fatalf("WaitForOutput() timeout: %v", err) + t.Fatalf("WaitForOutputSubmatch() timeout: %v", err) } - t.Log("Root capabilities:", want) + if len(matches) != 2 { + t.Fatalf("There should be a match for the whole line and the capability bitmask") + } + capString := matches[1] + t.Log("Root capabilities:", capString) + + // CAP_NET_RAW was in the capability set for the container, but was + // removed. However, `exec` does not remove it. Verify that it's not + // set in the container, then re-add it for comparison. + caps, err := strconv.ParseUint(capString, 16, 64) + if err != nil { + t.Fatalf("failed to convert capabilities %q: %v", capString, err) + } + if caps&(1< Date: Mon, 29 Apr 2019 14:03:04 -0700 Subject: Allow and document bug ids in gVisor codebase. PiperOrigin-RevId: 245818639 Change-Id: I03703ef0fb9b6675955637b9fe2776204c545789 --- CONTRIBUTING.md | 7 +++ pkg/cpuid/cpuid_test.go | 2 +- pkg/dhcp/client.go | 2 +- pkg/log/glog.go | 2 +- pkg/metric/metric.go | 4 +- pkg/segment/set.go | 2 +- pkg/segment/test/set_functions.go | 2 +- pkg/sentry/arch/arch.go | 2 +- pkg/sentry/arch/arch_amd64.go | 4 +- pkg/sentry/arch/arch_x86.go | 2 +- pkg/sentry/arch/signal_amd64.go | 6 +-- pkg/sentry/arch/stack.go | 6 +-- pkg/sentry/context/context.go | 2 +- pkg/sentry/control/proc.go | 2 +- pkg/sentry/fs/README.md | 2 +- pkg/sentry/fs/ashmem/area.go | 4 +- pkg/sentry/fs/binder/binder.go | 22 ++++---- pkg/sentry/fs/dentry.go | 2 +- pkg/sentry/fs/dirent.go | 8 +-- pkg/sentry/fs/file.go | 2 +- pkg/sentry/fs/file_overlay.go | 4 +- pkg/sentry/fs/fsutil/file.go | 8 +-- pkg/sentry/fs/fsutil/inode_cached.go | 4 +- pkg/sentry/fs/gofer/cache_policy.go | 4 +- pkg/sentry/fs/gofer/file.go | 2 +- pkg/sentry/fs/gofer/file_state.go | 2 +- pkg/sentry/fs/gofer/handles.go | 2 +- pkg/sentry/fs/gofer/inode.go | 6 +-- pkg/sentry/fs/gofer/inode_state.go | 2 +- pkg/sentry/fs/gofer/session.go | 2 +- pkg/sentry/fs/gofer/session_state.go | 2 +- pkg/sentry/fs/host/fs.go | 4 +- pkg/sentry/fs/host/inode.go | 10 ++-- pkg/sentry/fs/inode.go | 6 +-- pkg/sentry/fs/inode_operations.go | 2 +- pkg/sentry/fs/inode_overlay.go | 6 +-- pkg/sentry/fs/mount.go | 4 +- pkg/sentry/fs/mount_test.go | 2 +- pkg/sentry/fs/proc/README.md | 12 ++--- pkg/sentry/fs/proc/fds.go | 2 +- pkg/sentry/fs/proc/loadavg.go | 2 +- pkg/sentry/fs/proc/meminfo.go | 6 +-- pkg/sentry/fs/proc/mounts.go | 2 +- pkg/sentry/fs/proc/net.go | 2 +- pkg/sentry/fs/proc/stat.go | 12 ++--- pkg/sentry/fs/proc/sys_net.go | 2 +- pkg/sentry/fs/proc/task.go | 8 +-- pkg/sentry/fs/proc/version.go | 2 +- pkg/sentry/fs/ramfs/dir.go | 2 +- pkg/sentry/fs/tmpfs/fs.go | 2 +- pkg/sentry/fs/tmpfs/inode_file.go | 2 +- pkg/sentry/fs/tmpfs/tmpfs.go | 2 +- pkg/sentry/fs/tty/dir.go | 6 +-- pkg/sentry/fs/tty/fs.go | 2 +- pkg/sentry/fs/tty/master.go | 6 +-- pkg/sentry/fs/tty/slave.go | 6 +-- pkg/sentry/kernel/auth/credentials.go | 2 +- pkg/sentry/kernel/auth/user_namespace.go | 2 +- pkg/sentry/kernel/pending_signals.go | 2 +- pkg/sentry/kernel/ptrace.go | 4 +- pkg/sentry/kernel/rseq.go | 2 +- pkg/sentry/kernel/sched/cpuset.go | 2 +- pkg/sentry/kernel/semaphore/semaphore.go | 6 +-- pkg/sentry/kernel/shm/shm.go | 2 +- pkg/sentry/kernel/syscalls.go | 2 +- pkg/sentry/kernel/task_context.go | 2 +- pkg/sentry/kernel/task_exec.go | 2 +- pkg/sentry/kernel/task_exit.go | 4 +- pkg/sentry/kernel/task_identity.go | 2 +- pkg/sentry/kernel/task_run.go | 2 +- pkg/sentry/kernel/task_signals.go | 4 +- pkg/sentry/kernel/task_stop.go | 2 +- pkg/sentry/loader/loader.go | 2 +- pkg/sentry/loader/vdso.go | 6 +-- pkg/sentry/memmap/memmap.go | 2 +- pkg/sentry/mm/aio_context.go | 2 +- pkg/sentry/mm/procfs.go | 10 ++-- pkg/sentry/mm/special_mappable.go | 2 +- pkg/sentry/mm/syscalls.go | 6 +-- pkg/sentry/mm/vma.go | 2 +- pkg/sentry/platform/kvm/kvm_amd64_unsafe.go | 2 +- pkg/sentry/platform/platform.go | 2 +- pkg/sentry/platform/ptrace/subprocess.go | 2 +- pkg/sentry/platform/ring0/x86.go | 4 +- pkg/sentry/sighandling/sighandling.go | 2 +- pkg/sentry/sighandling/sighandling_unsafe.go | 2 +- pkg/sentry/socket/epsocket/epsocket.go | 32 ++++++------ pkg/sentry/socket/epsocket/save_restore.go | 2 +- pkg/sentry/socket/epsocket/stack.go | 2 +- pkg/sentry/socket/hostinet/socket.go | 2 +- pkg/sentry/socket/netlink/route/protocol.go | 8 +-- pkg/sentry/socket/netlink/socket.go | 10 ++-- pkg/sentry/socket/rpcinet/conn/conn.go | 2 +- pkg/sentry/socket/rpcinet/notifier/notifier.go | 4 +- pkg/sentry/socket/rpcinet/socket.go | 6 +-- pkg/sentry/socket/rpcinet/syscall_rpc.proto | 2 +- pkg/sentry/strace/strace.go | 2 +- pkg/sentry/syscalls/linux/error.go | 2 +- pkg/sentry/syscalls/linux/linux64.go | 60 +++++++++++----------- pkg/sentry/syscalls/linux/sys_aio.go | 2 +- pkg/sentry/syscalls/linux/sys_file.go | 4 +- pkg/sentry/syscalls/linux/sys_mmap.go | 4 +- pkg/sentry/syscalls/linux/sys_read.go | 2 +- pkg/sentry/syscalls/linux/sys_socket.go | 4 +- pkg/sentry/syscalls/linux/sys_thread.go | 2 +- pkg/sentry/syscalls/linux/sys_write.go | 4 +- pkg/sentry/time/calibrated_clock.go | 6 +-- pkg/sentry/time/parameters.go | 2 +- pkg/sentry/usermem/usermem.go | 4 +- pkg/sentry/watchdog/watchdog.go | 2 +- pkg/syserr/syserr.go | 10 ++-- pkg/tcpip/network/ipv4/icmp.go | 2 +- pkg/tcpip/network/ipv6/icmp.go | 4 +- pkg/tcpip/stack/nic.go | 6 +-- pkg/tcpip/stack/stack.go | 4 +- pkg/tcpip/stack/stack_global_state.go | 2 +- pkg/tcpip/stack/transport_test.go | 2 +- pkg/tcpip/tcpip.go | 2 +- pkg/tcpip/transport/raw/raw.go | 2 +- pkg/tcpip/transport/tcp/BUILD | 2 +- pkg/unet/unet.go | 2 +- pkg/unet/unet_test.go | 2 +- runsc/boot/controller.go | 4 +- runsc/boot/fs.go | 6 +-- runsc/boot/loader.go | 2 +- runsc/cmd/checkpoint.go | 2 +- runsc/container/container.go | 2 +- runsc/container/container_test.go | 4 +- runsc/sandbox/sandbox.go | 6 +-- runsc/specutils/specutils.go | 4 +- test/syscalls/BUILD | 6 +-- test/syscalls/build_defs.bzl | 4 +- test/syscalls/linux/32bit.cc | 14 ++--- test/syscalls/linux/aio.cc | 2 +- test/syscalls/linux/chmod.cc | 2 +- test/syscalls/linux/epoll.cc | 2 +- test/syscalls/linux/exec_binary.cc | 12 ++--- test/syscalls/linux/file_base.h | 4 +- test/syscalls/linux/ioctl.cc | 4 +- test/syscalls/linux/ip_socket_test_util.cc | 2 +- test/syscalls/linux/lseek.cc | 2 +- test/syscalls/linux/mkdir.cc | 2 +- test/syscalls/linux/mmap.cc | 18 +++---- test/syscalls/linux/open.cc | 2 +- test/syscalls/linux/partial_bad_buffer.cc | 18 +++---- test/syscalls/linux/pipe.cc | 6 +-- test/syscalls/linux/proc.cc | 32 ++++++------ test/syscalls/linux/proc_pid_smaps.cc | 2 +- test/syscalls/linux/ptrace.cc | 2 +- test/syscalls/linux/pwrite64.cc | 2 +- test/syscalls/linux/readv_socket.cc | 2 +- test/syscalls/linux/rtsignal.cc | 2 +- test/syscalls/linux/socket_inet_loopback.cc | 10 ++-- .../socket_ipv4_udp_unbound_external_networking.cc | 4 +- test/syscalls/linux/socket_netlink_route.cc | 4 +- test/syscalls/linux/socket_stream_blocking.cc | 2 +- test/syscalls/linux/socket_test_util.cc | 2 +- test/syscalls/linux/socket_unix.cc | 16 +++--- test/syscalls/linux/socket_unix_dgram.cc | 2 +- .../linux/socket_unix_dgram_non_blocking.cc | 2 +- test/syscalls/linux/socket_unix_non_stream.cc | 10 ++-- .../linux/socket_unix_unbound_seqpacket.cc | 2 +- test/syscalls/linux/socket_unix_unbound_stream.cc | 4 +- test/syscalls/linux/stat.cc | 2 +- test/syscalls/linux/stat_times.cc | 8 +-- test/syscalls/linux/tcp_socket.cc | 2 +- test/syscalls/linux/tkill.cc | 2 +- test/syscalls/linux/udp_bind.cc | 4 +- test/syscalls/linux/uidgid.cc | 2 +- test/syscalls/linux/utimes.cc | 4 +- test/syscalls/linux/wait.cc | 2 +- test/syscalls/linux/write.cc | 2 +- third_party/gvsync/downgradable_rwmutex_unsafe.go | 2 +- vdso/cycle_clock.h | 2 +- vdso/vdso_amd64.lds | 2 +- vdso/vdso_arm64.lds | 2 +- 176 files changed, 403 insertions(+), 396 deletions(-) (limited to 'runsc/specutils') diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6dafc595..238dd6665 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -126,6 +126,13 @@ change. When approved, the change will be submitted by a team member and automatically merged into the repository. +### Bug IDs + +Some TODOs and NOTEs sprinkled throughout the code have associated IDs of the +form b/1234. These correspond to bugs in our internal bug tracker. Eventually +these bugs will be moved to the GitHub Issues, but until then they can simply be +ignored. + ### The small print Contributions made by corporations are covered by a different agreement than the diff --git a/pkg/cpuid/cpuid_test.go b/pkg/cpuid/cpuid_test.go index 35e7b8e50..64ade1cbe 100644 --- a/pkg/cpuid/cpuid_test.go +++ b/pkg/cpuid/cpuid_test.go @@ -78,7 +78,7 @@ func TestTakeFeatureIntersection(t *testing.T) { } } -// TODO: Run this test on a very old platform, and make sure more +// TODO(b/73346484): Run this test on a very old platform, and make sure more // bits are enabled than just FPU and PAE. This test currently may not detect // if HostFeatureSet gives back junk bits. func TestHostFeatureSet(t *testing.T) { diff --git a/pkg/dhcp/client.go b/pkg/dhcp/client.go index 354205e63..2ba79be32 100644 --- a/pkg/dhcp/client.go +++ b/pkg/dhcp/client.go @@ -120,7 +120,7 @@ func (c *Client) Config() Config { // If the server sets a lease limit a timer is set to automatically // renew it. func (c *Client) Request(ctx context.Context, requestedAddr tcpip.Address) (cfg Config, reterr error) { - // TODO: remove calls to {Add,Remove}Address when they're no + // TODO(b/127321246): remove calls to {Add,Remove}Address when they're no // longer required to send and receive broadcast. if err := c.stack.AddAddressWithOptions(c.nicid, ipv4.ProtocolNumber, tcpipHeader.IPv4Any, stack.NeverPrimaryEndpoint); err != nil && err != tcpip.ErrDuplicateAddress { return Config{}, fmt.Errorf("dhcp: AddAddressWithOptions(): %s", err) diff --git a/pkg/log/glog.go b/pkg/log/glog.go index fbb58501b..24d5390d7 100644 --- a/pkg/log/glog.go +++ b/pkg/log/glog.go @@ -144,7 +144,7 @@ func (g GoogleEmitter) Emit(level Level, timestamp time.Time, format string, arg b.writeAll(pid) b.write(' ') - // FIXME: The caller, fabricated. This really sucks, but it + // FIXME(b/73383460): The caller, fabricated. This really sucks, but it // is unacceptable to put runtime.Callers() in the hot path. b.writeAll(caller) b.write(']') diff --git a/pkg/metric/metric.go b/pkg/metric/metric.go index 02af75974..e5eb95f89 100644 --- a/pkg/metric/metric.go +++ b/pkg/metric/metric.go @@ -44,8 +44,8 @@ var ( // // Metrics are not saved across save/restore and thus reset to zero on restore. // -// TODO: Support non-cumulative metrics. -// TODO: Support metric fields. +// TODO(b/67298402): Support non-cumulative metrics. +// TODO(b/67298427): Support metric fields. // type Uint64Metric struct { // value is the actual value of the metric. It must be accessed diff --git a/pkg/segment/set.go b/pkg/segment/set.go index a9a3b8875..74a916ea3 100644 --- a/pkg/segment/set.go +++ b/pkg/segment/set.go @@ -1270,7 +1270,7 @@ func segmentAfterPosition(n *node, i int) Iterator { } func zeroValueSlice(slice []Value) { - // TODO: check if Go is actually smart enough to optimize a + // TODO(jamieliu): check if Go is actually smart enough to optimize a // ClearValue that assigns nil to a memset here for i := range slice { Functions{}.ClearValue(&slice[i]) diff --git a/pkg/segment/test/set_functions.go b/pkg/segment/test/set_functions.go index 05ba5fbb9..41f649011 100644 --- a/pkg/segment/test/set_functions.go +++ b/pkg/segment/test/set_functions.go @@ -15,7 +15,7 @@ package segment // Basic numeric constants that we define because the math package doesn't. -// TODO: These should be Math.MaxInt64/MinInt64? +// TODO(nlacasse): These should be Math.MaxInt64/MinInt64? const ( maxInt = int(^uint(0) >> 1) minInt = -maxInt - 1 diff --git a/pkg/sentry/arch/arch.go b/pkg/sentry/arch/arch.go index 4cd7a9af5..16d8eb2b2 100644 --- a/pkg/sentry/arch/arch.go +++ b/pkg/sentry/arch/arch.go @@ -53,7 +53,7 @@ type FloatingPointData byte // Context provides architecture-dependent information for a specific thread. // -// NOTE: Currently we use uintptr here to refer to a generic native +// NOTE(b/34169503): Currently we use uintptr here to refer to a generic native // register value. While this will work for the foreseeable future, it isn't // strictly correct. We may want to create some abstraction that makes this // more clear or enables us to store values of arbitrary widths. This is diff --git a/pkg/sentry/arch/arch_amd64.go b/pkg/sentry/arch/arch_amd64.go index 2507774f7..7ec2f2c84 100644 --- a/pkg/sentry/arch/arch_amd64.go +++ b/pkg/sentry/arch/arch_amd64.go @@ -305,7 +305,7 @@ func (c *context64) PtracePeekUser(addr uintptr) (interface{}, error) { buf := binary.Marshal(nil, usermem.ByteOrder, c.ptraceGetRegs()) return c.Native(uintptr(usermem.ByteOrder.Uint64(buf[addr:]))), nil } - // TODO: debug registers + // TODO(b/34088053): debug registers return c.Native(0), nil } @@ -320,6 +320,6 @@ func (c *context64) PtracePokeUser(addr, data uintptr) error { _, err := c.PtraceSetRegs(bytes.NewBuffer(buf)) return err } - // TODO: debug registers + // TODO(b/34088053): debug registers return nil } diff --git a/pkg/sentry/arch/arch_x86.go b/pkg/sentry/arch/arch_x86.go index c8bf0e7f2..4305fe2cb 100644 --- a/pkg/sentry/arch/arch_x86.go +++ b/pkg/sentry/arch/arch_x86.go @@ -306,7 +306,7 @@ func (s *State) ptraceGetRegs() syscall.PtraceRegs { // FS/GS_TLS_SEL when fs_base/gs_base is a 64-bit value. (We do the // same in PtraceSetRegs.) // - // TODO: Remove this fixup since newer Linux + // TODO(gvisor.dev/issue/168): Remove this fixup since newer Linux // doesn't have this behavior anymore. if regs.Fs == 0 && regs.Fs_base <= 0xffffffff { regs.Fs = _FS_TLS_SEL diff --git a/pkg/sentry/arch/signal_amd64.go b/pkg/sentry/arch/signal_amd64.go index c9de36897..7f76eba27 100644 --- a/pkg/sentry/arch/signal_amd64.go +++ b/pkg/sentry/arch/signal_amd64.go @@ -319,7 +319,7 @@ func (c *context64) NewSignalStack() NativeSignalStack { // From Linux 'arch/x86/include/uapi/asm/sigcontext.h' the following is the // size of the magic cookie at the end of the xsave frame. // -// NOTE: Currently we don't actually populate the fpstate +// NOTE(b/33003106#comment11): Currently we don't actually populate the fpstate // on the signal stack. const _FP_XSTATE_MAGIC2_SIZE = 4 @@ -392,7 +392,7 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt Sigset: sigset, } - // TODO: Set SignalContext64.Err, Trapno, and Cr2 + // TODO(gvisor.dev/issue/159): Set SignalContext64.Err, Trapno, and Cr2 // based on the fault that caused the signal. For now, leave Err and // Trapno unset and assume CR2 == info.Addr() for SIGSEGVs and // SIGBUSes. @@ -505,7 +505,7 @@ func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, SignalSt l := len(c.sigFPState) if l > 0 { c.x86FPState = c.sigFPState[l-1] - // NOTE: State save requires that any slice + // NOTE(cl/133042258): State save requires that any slice // elements from '[len:cap]' to be zero value. c.sigFPState[l-1] = nil c.sigFPState = c.sigFPState[0 : l-1] diff --git a/pkg/sentry/arch/stack.go b/pkg/sentry/arch/stack.go index f2cfb0426..2e33ccdf5 100644 --- a/pkg/sentry/arch/stack.go +++ b/pkg/sentry/arch/stack.go @@ -97,7 +97,7 @@ func (s *Stack) Push(vals ...interface{}) (usermem.Addr, error) { if c < 0 { return 0, fmt.Errorf("bad binary.Size for %T", v) } - // TODO: Use a real context.Context. + // TODO(b/38173783): Use a real context.Context. n, err := usermem.CopyObjectOut(context.Background(), s.IO, s.Bottom-usermem.Addr(c), norm, usermem.IOOpts{}) if err != nil || c != n { return 0, err @@ -121,11 +121,11 @@ func (s *Stack) Pop(vals ...interface{}) (usermem.Addr, error) { var err error if isVaddr { value := s.Arch.Native(uintptr(0)) - // TODO: Use a real context.Context. + // TODO(b/38173783): Use a real context.Context. n, err = usermem.CopyObjectIn(context.Background(), s.IO, s.Bottom, value, usermem.IOOpts{}) *vaddr = usermem.Addr(s.Arch.Value(value)) } else { - // TODO: Use a real context.Context. + // TODO(b/38173783): Use a real context.Context. n, err = usermem.CopyObjectIn(context.Background(), s.IO, s.Bottom, v, usermem.IOOpts{}) } if err != nil { diff --git a/pkg/sentry/context/context.go b/pkg/sentry/context/context.go index 7ed6a5e8a..eefc3e1b4 100644 --- a/pkg/sentry/context/context.go +++ b/pkg/sentry/context/context.go @@ -114,7 +114,7 @@ var bgContext = &logContext{Logger: log.Log()} // Background returns an empty context using the default logger. // // Users should be wary of using a Background context. Please tag any use with -// FIXME and a note to remove this use. +// FIXME(b/38173783) and a note to remove this use. // // Generally, one should use the Task as their context when available, or avoid // having to use a context in places where a Task is unavailable. diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go index e848def14..aca2267a7 100644 --- a/pkg/sentry/control/proc.go +++ b/pkg/sentry/control/proc.go @@ -261,7 +261,7 @@ func (proc *Proc) Ps(args *PsArgs, out *string) error { } // Process contains information about a single process in a Sandbox. -// TODO: Implement TTY field. +// TODO(b/117881927): Implement TTY field. type Process struct { UID auth.KUID `json:"uid"` PID kernel.ThreadID `json:"pid"` diff --git a/pkg/sentry/fs/README.md b/pkg/sentry/fs/README.md index a88a0cd3a..f53ed3eaa 100644 --- a/pkg/sentry/fs/README.md +++ b/pkg/sentry/fs/README.md @@ -59,7 +59,7 @@ two categories: The first is always necessary to save and restore. An application may never have any open file descriptors, but across save and restore it should see a coherent -view of any mount namespace. NOTE: Currently only one "initial" +view of any mount namespace. NOTE(b/63601033): Currently only one "initial" mount namespace is supported. The second is so that system calls across save and restore are coherent with diff --git a/pkg/sentry/fs/ashmem/area.go b/pkg/sentry/fs/ashmem/area.go index 651cbc164..1f61c5711 100644 --- a/pkg/sentry/fs/ashmem/area.go +++ b/pkg/sentry/fs/ashmem/area.go @@ -240,7 +240,7 @@ func (a *Area) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArgume return 0, syserror.EINVAL } - // TODO: If personality flag + // TODO(b/30946773,gvisor.dev/issue/153): If personality flag // READ_IMPLIES_EXEC is set, set PROT_EXEC if PORT_READ is set. a.perms = perms @@ -290,7 +290,7 @@ func (a *Area) pinOperation(pin linux.AshmemPin, op uint32) (uintptr, error) { return linux.AshmemNotPurged, nil case linux.AshmemUnpinIoctl: - // TODO: Implement purge on unpin. + // TODO(b/30946773): Implement purge on unpin. a.pb.UnpinRange(r) return 0, nil diff --git a/pkg/sentry/fs/binder/binder.go b/pkg/sentry/fs/binder/binder.go index a41b5dcae..d9f1559de 100644 --- a/pkg/sentry/fs/binder/binder.go +++ b/pkg/sentry/fs/binder/binder.go @@ -69,7 +69,7 @@ func NewDevice(ctx context.Context, owner fs.FileOwner, fp fs.FilePermissions) * // GetFile implements fs.InodeOperations.GetFile. // -// TODO: Add functionality to GetFile: Additional fields will be +// TODO(b/30946773): Add functionality to GetFile: Additional fields will be // needed in the Device structure, initialize them here. Also, Device will need // to keep track of the created Procs in order to implement BINDER_READ_WRITE // ioctl. @@ -133,7 +133,7 @@ func (bp *Proc) Write(ctx context.Context, file *fs.File, src usermem.IOSequence // Flush implements fs.FileOperations.Flush. // -// TODO: Implement. +// TODO(b/30946773): Implement. func (bp *Proc) Flush(ctx context.Context, file *fs.File) error { return nil } @@ -149,7 +149,7 @@ func (bp *Proc) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.M } opts.MaxPerms.Write = false - // TODO: Binder sets VM_DONTCOPY, preventing the created vma + // TODO(b/30946773): Binder sets VM_DONTCOPY, preventing the created vma // from being copied across fork(), but we don't support this yet. As // a result, MMs containing a Binder mapping cannot be forked (MM.Fork will // fail when AddMapping returns EBUSY). @@ -159,7 +159,7 @@ func (bp *Proc) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.M // Ioctl implements fs.FileOperations.Ioctl. // -// TODO: Implement. +// TODO(b/30946773): Implement. func (bp *Proc) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { // Switch on ioctl request. switch uint32(args[1].Int()) { @@ -173,22 +173,22 @@ func (bp *Proc) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArgum }) return 0, err case linux.BinderWriteReadIoctl: - // TODO: Implement. + // TODO(b/30946773): Implement. fallthrough case linux.BinderSetIdleTimeoutIoctl: - // TODO: Implement. + // TODO(b/30946773): Implement. fallthrough case linux.BinderSetMaxThreadsIoctl: - // TODO: Implement. + // TODO(b/30946773): Implement. fallthrough case linux.BinderSetIdlePriorityIoctl: - // TODO: Implement. + // TODO(b/30946773): Implement. fallthrough case linux.BinderSetContextMgrIoctl: - // TODO: Implement. + // TODO(b/30946773): Implement. fallthrough case linux.BinderThreadExitIoctl: - // TODO: Implement. + // TODO(b/30946773): Implement. return 0, syserror.ENOSYS default: // Ioctls irrelevant to Binder. @@ -228,7 +228,7 @@ func (bp *Proc) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, // Translate implements memmap.Mappable.Translate. func (bp *Proc) Translate(ctx context.Context, required, optional memmap.MappableRange, at usermem.AccessType) ([]memmap.Translation, error) { - // TODO: In addition to the page initially allocated and mapped + // TODO(b/30946773): In addition to the page initially allocated and mapped // in AddMapping (Linux: binder_mmap), Binder allocates and maps pages for // each transaction (Linux: binder_ioctl => binder_ioctl_write_read => // binder_thread_write => binder_transaction => binder_alloc_buf => diff --git a/pkg/sentry/fs/dentry.go b/pkg/sentry/fs/dentry.go index 4879df4d6..29fb155a4 100644 --- a/pkg/sentry/fs/dentry.go +++ b/pkg/sentry/fs/dentry.go @@ -83,7 +83,7 @@ type DirCtx struct { attrs map[string]DentAttr // DirCursor is the directory cursor. - // TODO: Once Handles are removed this can just live in the + // TODO(b/67778717): Once Handles are removed this can just live in the // respective FileOperations implementations and not need to get // plumbed everywhere. DirCursor *string diff --git a/pkg/sentry/fs/dirent.go b/pkg/sentry/fs/dirent.go index 4bcdf530a..54fc11fe1 100644 --- a/pkg/sentry/fs/dirent.go +++ b/pkg/sentry/fs/dirent.go @@ -318,7 +318,7 @@ func (d *Dirent) SyncAll(ctx context.Context) { // There is nothing to sync for a read-only filesystem. if !d.Inode.MountSource.Flags.ReadOnly { - // FIXME: This should be a mount traversal, not a + // FIXME(b/34856369): This should be a mount traversal, not a // Dirent traversal, because some Inodes that need to be synced // may no longer be reachable by name (after sys_unlink). // @@ -1506,7 +1506,7 @@ func Rename(ctx context.Context, root *Dirent, oldParent *Dirent, oldName string } // Are we frozen? - // TODO: Is this the right errno? + // TODO(jamieliu): Is this the right errno? if oldParent.frozen && !oldParent.Inode.IsVirtual() { return syscall.ENOENT } @@ -1565,7 +1565,7 @@ func Rename(ctx context.Context, root *Dirent, oldParent *Dirent, oldName string } else { // Check constraints on the dirent being replaced. - // NOTE: We don't want to keep replaced alive + // NOTE(b/111808347): We don't want to keep replaced alive // across the Rename, so must call DecRef manually (no defer). // Check that we can delete replaced. @@ -1606,7 +1606,7 @@ func Rename(ctx context.Context, root *Dirent, oldParent *Dirent, oldName string // Allow the file system to drop extra references on replaced. replaced.dropExtendedReference() - // NOTE: Keeping a dirent + // NOTE(b/31798319,b/31867149,b/31867671): Keeping a dirent // open across renames is currently broken for multiple // reasons, so we flush all references on the replaced node and // its children. diff --git a/pkg/sentry/fs/file.go b/pkg/sentry/fs/file.go index 2c2126f17..5d5026661 100644 --- a/pkg/sentry/fs/file.go +++ b/pkg/sentry/fs/file.go @@ -65,7 +65,7 @@ const FileMaxOffset = math.MaxInt64 // under a single abortable mutex which also synchronizes lseek(2), read(2), // and write(2). // -// FIXME: Split synchronization from cancellation. +// FIXME(b/38451980): Split synchronization from cancellation. // // +stateify savable type File struct { diff --git a/pkg/sentry/fs/file_overlay.go b/pkg/sentry/fs/file_overlay.go index e1f02f0f4..6e680f0a4 100644 --- a/pkg/sentry/fs/file_overlay.go +++ b/pkg/sentry/fs/file_overlay.go @@ -160,7 +160,7 @@ func (f *overlayFileOperations) Seek(ctx context.Context, file *File, whence See // If this was a seek on a directory, we must update the cursor. if seekDir && whence == SeekSet && offset == 0 { // Currently only seeking to 0 on a directory is supported. - // FIXME: Lift directory seeking limitations. + // FIXME(b/33075855): Lift directory seeking limitations. f.dirCursor = "" } return n, nil @@ -329,7 +329,7 @@ func (*overlayFileOperations) ConfigureMMap(ctx context.Context, file *File, opt if !o.isMappableLocked() { return syserror.ENODEV } - // FIXME: This is a copy/paste of fsutil.GenericConfigureMMap, + // FIXME(jamieliu): This is a copy/paste of fsutil.GenericConfigureMMap, // which we can't use because the overlay implementation is in package fs, // so depending on fs/fsutil would create a circular dependency. Move // overlay to fs/overlay. diff --git a/pkg/sentry/fs/fsutil/file.go b/pkg/sentry/fs/fsutil/file.go index df34dc788..42afdd11c 100644 --- a/pkg/sentry/fs/fsutil/file.go +++ b/pkg/sentry/fs/fsutil/file.go @@ -36,7 +36,7 @@ func (FileNoopRelease) Release() {} // // Currently only seeking to 0 on a directory is supported. // -// FIXME: Lift directory seeking limitations. +// FIXME(b/33075855): Lift directory seeking limitations. func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64, dirCursor *string) (int64, error) { inode := file.Dirent.Inode current := file.Offset() @@ -50,7 +50,7 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence, if fs.IsCharDevice(inode.StableAttr) { // Ignore seek requests. // - // FIXME: This preserves existing + // FIXME(b/34716638): This preserves existing // behavior but is not universally correct. return 0, nil } @@ -104,7 +104,7 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence, return current, syserror.EINVAL } return sz + offset, nil - // FIXME: This is not universally correct. + // FIXME(b/34778850): This is not universally correct. // Remove SpecialDirectory. case fs.SpecialDirectory: if offset != 0 { @@ -112,7 +112,7 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence, } // SEEK_END to 0 moves the directory "cursor" to the end. // - // FIXME: The ensures that after the seek, + // FIXME(b/35442290): The ensures that after the seek, // reading on the directory will get EOF. But it is not // correct in general because the directory can grow in // size; attempting to read those new entries will be diff --git a/pkg/sentry/fs/fsutil/inode_cached.go b/pkg/sentry/fs/fsutil/inode_cached.go index b690cfe93..ba33b9912 100644 --- a/pkg/sentry/fs/fsutil/inode_cached.go +++ b/pkg/sentry/fs/fsutil/inode_cached.go @@ -479,7 +479,7 @@ func (c *CachingInodeOperations) Read(ctx context.Context, file *fs.File, dst us // common: getting a return value of 0 from a read syscall is the only way // to detect EOF. // - // TODO: Separate out c.attr.Size and use atomics instead of + // TODO(jamieliu): Separate out c.attr.Size and use atomics instead of // c.dataMu. c.dataMu.RLock() size := c.attr.Size @@ -776,7 +776,7 @@ func (c *CachingInodeOperations) Translate(ctx context.Context, required, option var translatedEnd uint64 for seg := c.cache.FindSegment(required.Start); seg.Ok() && seg.Start() < required.End; seg, _ = seg.NextNonEmpty() { segMR := seg.Range().Intersect(optional) - // TODO: Make Translations writable even if writability is + // TODO(jamieliu): Make Translations writable even if writability is // not required if already kept-dirty by another writable translation. perms := usermem.AccessType{ Read: true, diff --git a/pkg/sentry/fs/gofer/cache_policy.go b/pkg/sentry/fs/gofer/cache_policy.go index d7fbb71b7..51c573aef 100644 --- a/pkg/sentry/fs/gofer/cache_policy.go +++ b/pkg/sentry/fs/gofer/cache_policy.go @@ -136,7 +136,7 @@ func (cp cachePolicy) revalidate(ctx context.Context, name string, parent, child // Walk from parent to child again. // - // TODO: If we have a directory FD in the parent + // TODO(b/112031682): If we have a directory FD in the parent // inodeOperations, then we can use fstatat(2) to get the inode // attributes instead of making this RPC. qids, _, mask, attr, err := parentIops.fileState.file.walkGetAttr(ctx, []string{name}) @@ -171,7 +171,7 @@ func (cp cachePolicy) keep(d *fs.Dirent) bool { return false } sattr := d.Inode.StableAttr - // NOTE: Only cache files, directories, and symlinks. + // NOTE(b/31979197): Only cache files, directories, and symlinks. return fs.IsFile(sattr) || fs.IsDir(sattr) || fs.IsSymlink(sattr) } diff --git a/pkg/sentry/fs/gofer/file.go b/pkg/sentry/fs/gofer/file.go index 80d1e08a6..35caa42cd 100644 --- a/pkg/sentry/fs/gofer/file.go +++ b/pkg/sentry/fs/gofer/file.go @@ -297,7 +297,7 @@ func (f *fileOperations) Flush(ctx context.Context, file *fs.File) error { // We do this because some p9 server implementations of Flush are // over-zealous. // - // FIXME: weaken these implementations and remove this check. + // FIXME(edahlgren): weaken these implementations and remove this check. if !file.Flags().Write { return nil } diff --git a/pkg/sentry/fs/gofer/file_state.go b/pkg/sentry/fs/gofer/file_state.go index f770ca4ea..d0c64003c 100644 --- a/pkg/sentry/fs/gofer/file_state.go +++ b/pkg/sentry/fs/gofer/file_state.go @@ -28,7 +28,7 @@ func (f *fileOperations) afterLoad() { // Manually load the open handles. var err error - // TODO: Context is not plumbed to save/restore. + // TODO(b/38173783): Context is not plumbed to save/restore. f.handles, err = f.inodeOperations.fileState.getHandles(context.Background(), f.flags) if err != nil { return fmt.Errorf("failed to re-open handle: %v", err) diff --git a/pkg/sentry/fs/gofer/handles.go b/pkg/sentry/fs/gofer/handles.go index f32e99ce0..0b33e80c3 100644 --- a/pkg/sentry/fs/gofer/handles.go +++ b/pkg/sentry/fs/gofer/handles.go @@ -49,7 +49,7 @@ func (h *handles) DecRef() { log.Warningf("error closing host file: %v", err) } } - // FIXME: Context is not plumbed here. + // FIXME(b/38173783): Context is not plumbed here. if err := h.File.close(context.Background()); err != nil { log.Warningf("error closing p9 file: %v", err) } diff --git a/pkg/sentry/fs/gofer/inode.go b/pkg/sentry/fs/gofer/inode.go index 29af1010c..1181a24cc 100644 --- a/pkg/sentry/fs/gofer/inode.go +++ b/pkg/sentry/fs/gofer/inode.go @@ -570,13 +570,13 @@ func init() { } // AddLink implements InodeOperations.AddLink, but is currently a noop. -// FIXME: Remove this from InodeOperations altogether. +// FIXME(b/63117438): Remove this from InodeOperations altogether. func (*inodeOperations) AddLink() {} // DropLink implements InodeOperations.DropLink, but is currently a noop. -// FIXME: Remove this from InodeOperations altogether. +// FIXME(b/63117438): Remove this from InodeOperations altogether. func (*inodeOperations) DropLink() {} // NotifyStatusChange implements fs.InodeOperations.NotifyStatusChange. -// FIXME: Remove this from InodeOperations altogether. +// FIXME(b/63117438): Remove this from InodeOperations altogether. func (i *inodeOperations) NotifyStatusChange(ctx context.Context) {} diff --git a/pkg/sentry/fs/gofer/inode_state.go b/pkg/sentry/fs/gofer/inode_state.go index ad4d3df58..44d76ba9f 100644 --- a/pkg/sentry/fs/gofer/inode_state.go +++ b/pkg/sentry/fs/gofer/inode_state.go @@ -123,7 +123,7 @@ func (i *inodeFileState) afterLoad() { // beforeSave. return fmt.Errorf("failed to find path for inode number %d. Device %s contains %s", i.sattr.InodeID, i.s.connID, fs.InodeMappings(i.s.inodeMappings)) } - // TODO: Context is not plumbed to save/restore. + // TODO(b/38173783): Context is not plumbed to save/restore. ctx := &dummyClockContext{context.Background()} _, i.file, err = i.s.attach.walk(ctx, splitAbsolutePath(name)) diff --git a/pkg/sentry/fs/gofer/session.go b/pkg/sentry/fs/gofer/session.go index ed5147c65..4ed688ce5 100644 --- a/pkg/sentry/fs/gofer/session.go +++ b/pkg/sentry/fs/gofer/session.go @@ -134,7 +134,7 @@ type session struct { // socket files. This allows unix domain sockets to be used with paths that // belong to a gofer. // - // TODO: there are few possible races with someone stat'ing the + // TODO(b/77154739): there are few possible races with someone stat'ing the // file and another deleting it concurrently, where the file will not be // reported as socket file. endpoints *endpointMaps `state:"wait"` diff --git a/pkg/sentry/fs/gofer/session_state.go b/pkg/sentry/fs/gofer/session_state.go index 0ad5d63b5..b1f299be5 100644 --- a/pkg/sentry/fs/gofer/session_state.go +++ b/pkg/sentry/fs/gofer/session_state.go @@ -104,7 +104,7 @@ func (s *session) afterLoad() { // If private unix sockets are enabled, create and fill the session's endpoint // maps. if opts.privateunixsocket { - // TODO: Context is not plumbed to save/restore. + // TODO(b/38173783): Context is not plumbed to save/restore. ctx := &dummyClockContext{context.Background()} if err = s.restoreEndpointMaps(ctx); err != nil { diff --git a/pkg/sentry/fs/host/fs.go b/pkg/sentry/fs/host/fs.go index 800649211..de349a41a 100644 --- a/pkg/sentry/fs/host/fs.go +++ b/pkg/sentry/fs/host/fs.go @@ -87,7 +87,7 @@ func (f *Filesystem) Mount(ctx context.Context, _ string, flags fs.MountSourceFl options := fs.GenericMountSourceOptions(data) // Grab the whitelist if one was specified. - // TODO: require another option "testonly" in order to allow + // TODO(edahlgren/mpratt/hzy): require another option "testonly" in order to allow // no whitelist. if wl, ok := options[whitelistKey]; ok { f.paths = strings.Split(wl, "|") @@ -320,7 +320,7 @@ func (m *superOperations) SaveInodeMapping(inode *fs.Inode, path string) { // Keep implements fs.MountSourceOperations.Keep. // -// TODO: It is possible to change the permissions on a +// TODO(b/72455313,b/77596690): It is possible to change the permissions on a // host file while it is in the dirent cache (say from RO to RW), but it is not // possible to re-open the file with more relaxed permissions, since the host // FD is already open and stored in the inode. diff --git a/pkg/sentry/fs/host/inode.go b/pkg/sentry/fs/host/inode.go index 2030edcb4..69c648f67 100644 --- a/pkg/sentry/fs/host/inode.go +++ b/pkg/sentry/fs/host/inode.go @@ -95,7 +95,7 @@ type inodeFileState struct { // ReadToBlocksAt implements fsutil.CachedFileObject.ReadToBlocksAt. func (i *inodeFileState) ReadToBlocksAt(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error) { - // TODO: Using safemem.FromIOReader here is wasteful for two + // TODO(jamieliu): Using safemem.FromIOReader here is wasteful for two // reasons: // // - Using preadv instead of iterated preads saves on host system calls. @@ -325,7 +325,7 @@ func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.Fi // canMap returns true if this fs.Inode can be memory mapped. func canMap(inode *fs.Inode) bool { - // FIXME: Some obscure character devices can be mapped. + // FIXME(b/38213152): Some obscure character devices can be mapped. return fs.IsFile(inode.StableAttr) } @@ -428,15 +428,15 @@ func (i *inodeOperations) StatFS(context.Context) (fs.Info, error) { } // AddLink implements fs.InodeOperations.AddLink. -// FIXME: Remove this from InodeOperations altogether. +// FIXME(b/63117438): Remove this from InodeOperations altogether. func (i *inodeOperations) AddLink() {} // DropLink implements fs.InodeOperations.DropLink. -// FIXME: Remove this from InodeOperations altogether. +// FIXME(b/63117438): Remove this from InodeOperations altogether. func (i *inodeOperations) DropLink() {} // NotifyStatusChange implements fs.InodeOperations.NotifyStatusChange. -// FIXME: Remove this from InodeOperations altogether. +// FIXME(b/63117438): Remove this from InodeOperations altogether. func (i *inodeOperations) NotifyStatusChange(ctx context.Context) {} // readdirAll returns all of the directory entries in i. diff --git a/pkg/sentry/fs/inode.go b/pkg/sentry/fs/inode.go index d82f9740e..fe411a766 100644 --- a/pkg/sentry/fs/inode.go +++ b/pkg/sentry/fs/inode.go @@ -93,10 +93,10 @@ func (i *Inode) DecRef() { // destroy releases the Inode and releases the msrc reference taken. func (i *Inode) destroy() { - // FIXME: Context is not plumbed here. + // FIXME(b/38173783): Context is not plumbed here. ctx := context.Background() if err := i.WriteOut(ctx); err != nil { - // FIXME: Mark as warning again once noatime is + // FIXME(b/65209558): Mark as warning again once noatime is // properly supported. log.Debugf("Inode %+v, failed to sync all metadata: %v", i.StableAttr, err) } @@ -359,7 +359,7 @@ func (i *Inode) Getlink(ctx context.Context) (*Dirent, error) { // AddLink calls i.InodeOperations.AddLink. func (i *Inode) AddLink() { if i.overlay != nil { - // FIXME: Remove this from InodeOperations altogether. + // FIXME(b/63117438): Remove this from InodeOperations altogether. // // This interface is only used by ramfs to update metadata of // children. These filesystems should _never_ have overlay diff --git a/pkg/sentry/fs/inode_operations.go b/pkg/sentry/fs/inode_operations.go index ceacc7659..ff8b75f31 100644 --- a/pkg/sentry/fs/inode_operations.go +++ b/pkg/sentry/fs/inode_operations.go @@ -118,7 +118,7 @@ type InodeOperations interface { // // The caller must ensure that this operation is permitted. // - // TODO: merge Remove and RemoveDirectory, Remove + // TODO(b/67778723): merge Remove and RemoveDirectory, Remove // just needs a type flag. Remove(ctx context.Context, dir *Inode, name string) error diff --git a/pkg/sentry/fs/inode_overlay.go b/pkg/sentry/fs/inode_overlay.go index 254646176..bda3e1861 100644 --- a/pkg/sentry/fs/inode_overlay.go +++ b/pkg/sentry/fs/inode_overlay.go @@ -142,7 +142,7 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name } else { // If we have something from the upper, we can only use it if the types // match. - // NOTE: Allow SpecialDirectories and Directories to merge. + // NOTE(b/112312863): Allow SpecialDirectories and Directories to merge. // This is needed to allow submounts in /proc and /sys. if upperInode.StableAttr.Type == child.Inode.StableAttr.Type || (IsDir(upperInode.StableAttr) && IsDir(child.Inode.StableAttr)) { @@ -226,7 +226,7 @@ func overlayCreate(ctx context.Context, o *overlayEntry, parent *Dirent, name st return nil, err } - // NOTE: Replace the Dirent with a transient Dirent, since + // NOTE(b/71766861): Replace the Dirent with a transient Dirent, since // we are about to create the real Dirent: an overlay Dirent. // // This ensures the *fs.File returned from overlayCreate is in the same @@ -338,7 +338,7 @@ func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, rena // directory will appear empty in the upper fs, which will then // allow the rename to proceed when it should return ENOTEMPTY. // - // NOTE: Ideally, we'd just pass in the replaced + // NOTE(b/111808347): Ideally, we'd just pass in the replaced // Dirent from Rename, but we must drop the reference on // replaced before we make the rename call, so Rename can't // pass the Dirent to the Inode without significantly diff --git a/pkg/sentry/fs/mount.go b/pkg/sentry/fs/mount.go index 1e245ae5f..4d1693204 100644 --- a/pkg/sentry/fs/mount.go +++ b/pkg/sentry/fs/mount.go @@ -42,7 +42,7 @@ type DirentOperations interface { // MountSourceOperations contains filesystem specific operations. type MountSourceOperations interface { - // TODO: Add: + // TODO(b/67778729): Add: // BlockSize() int64 // FS() Filesystem @@ -101,7 +101,7 @@ func (i InodeMappings) String() string { // amalgamation implies that a mount source cannot be shared by multiple mounts // (e.g. cannot be mounted at different locations). // -// TODO: Move mount-specific information out of MountSource. +// TODO(b/63601033): Move mount-specific information out of MountSource. // // +stateify savable type MountSource struct { diff --git a/pkg/sentry/fs/mount_test.go b/pkg/sentry/fs/mount_test.go index 269d6b9da..d7605b2c9 100644 --- a/pkg/sentry/fs/mount_test.go +++ b/pkg/sentry/fs/mount_test.go @@ -33,7 +33,7 @@ func cacheReallyContains(cache *DirentCache, d *Dirent) bool { } // TestMountSourceOnlyCachedOnce tests that a Dirent that is mounted over only ends -// up in a single Dirent Cache. NOTE: Having a dirent in multiple +// up in a single Dirent Cache. NOTE(b/63848693): Having a dirent in multiple // caches causes major consistency issues. func TestMountSourceOnlyCachedOnce(t *testing.T) { ctx := contexttest.Context(t) diff --git a/pkg/sentry/fs/proc/README.md b/pkg/sentry/fs/proc/README.md index 3cc5f197c..5d4ec6c7b 100644 --- a/pkg/sentry/fs/proc/README.md +++ b/pkg/sentry/fs/proc/README.md @@ -91,7 +91,7 @@ CPU.IO utilization in last 10 minutes | Always zero Num currently running processes | Always zero Total num processes | Always zero -TODO: Populate the columns with accurate statistics. +TODO(b/62345059): Populate the columns with accurate statistics. ### meminfo @@ -128,12 +128,12 @@ Field name | Notes Buffers | Always zero, no block devices SwapCache | Always zero, no swap Inactive(anon) | Always zero, see SwapCache -Unevictable | Always zero TODO -Mlocked | Always zero TODO +Unevictable | Always zero TODO(b/31823263) +Mlocked | Always zero TODO(b/31823263) SwapTotal | Always zero, no swap SwapFree | Always zero, no swap -Dirty | Always zero TODO -Writeback | Always zero TODO +Dirty | Always zero TODO(b/31823263) +Writeback | Always zero TODO(b/31823263) MemAvailable | Uses the same value as MemFree since there is no swap. Slab | Missing SReclaimable | Missing @@ -185,7 +185,7 @@ softirq 0 0 0 0 0 0 0 0 0 0 0 All fields except for `btime` are always zero. -TODO: Populate with accurate fields. +TODO(b/37226836): Populate with accurate fields. ### sys diff --git a/pkg/sentry/fs/proc/fds.go b/pkg/sentry/fs/proc/fds.go index 25da06f5d..f2329e623 100644 --- a/pkg/sentry/fs/proc/fds.go +++ b/pkg/sentry/fs/proc/fds.go @@ -258,7 +258,7 @@ func newFdInfoDir(t *kernel.Task, msrc *fs.MountSource) *fs.Inode { // Lookup loads an fd in /proc/TID/fdinfo into a Dirent. func (fdid *fdInfoDir) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) { inode, err := walkDescriptors(fdid.t, p, func(file *fs.File, fdFlags kernel.FDFlags) *fs.Inode { - // TODO: Using a static inode here means that the + // TODO(b/121266871): Using a static inode here means that the // data can be out-of-date if, for instance, the flags on the // FD change before we read this file. We should switch to // generating the data on Read(). Also, we should include pos, diff --git a/pkg/sentry/fs/proc/loadavg.go b/pkg/sentry/fs/proc/loadavg.go index 78f3a1dc0..3ee0e570a 100644 --- a/pkg/sentry/fs/proc/loadavg.go +++ b/pkg/sentry/fs/proc/loadavg.go @@ -40,7 +40,7 @@ func (d *loadavgData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) var buf bytes.Buffer - // TODO: Include real data in fields. + // TODO(b/62345059): Include real data in fields. // Column 1-3: CPU and IO utilization of the last 1, 5, and 10 minute periods. // Column 4-5: currently running processes and the total number of processes. // Column 6: the last process ID used. diff --git a/pkg/sentry/fs/proc/meminfo.go b/pkg/sentry/fs/proc/meminfo.go index 620e93ce3..75cbf3e77 100644 --- a/pkg/sentry/fs/proc/meminfo.go +++ b/pkg/sentry/fs/proc/meminfo.go @@ -58,7 +58,7 @@ func (d *meminfoData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) fmt.Fprintf(&buf, "MemTotal: %8d kB\n", totalSize/1024) memFree := (totalSize - totalUsage) / 1024 // We use MemFree as MemAvailable because we don't swap. - // TODO: When reclaim is implemented the value of MemAvailable + // TODO(rahat): When reclaim is implemented the value of MemAvailable // should change. fmt.Fprintf(&buf, "MemFree: %8d kB\n", memFree) fmt.Fprintf(&buf, "MemAvailable: %8d kB\n", memFree) @@ -72,8 +72,8 @@ func (d *meminfoData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) fmt.Fprintf(&buf, "Inactive(anon): 0 kB\n") fmt.Fprintf(&buf, "Active(file): %8d kB\n", activeFile/1024) fmt.Fprintf(&buf, "Inactive(file): %8d kB\n", inactiveFile/1024) - fmt.Fprintf(&buf, "Unevictable: 0 kB\n") // TODO - fmt.Fprintf(&buf, "Mlocked: 0 kB\n") // TODO + fmt.Fprintf(&buf, "Unevictable: 0 kB\n") // TODO(b/31823263) + fmt.Fprintf(&buf, "Mlocked: 0 kB\n") // TODO(b/31823263) fmt.Fprintf(&buf, "SwapTotal: 0 kB\n") fmt.Fprintf(&buf, "SwapFree: 0 kB\n") fmt.Fprintf(&buf, "Dirty: 0 kB\n") diff --git a/pkg/sentry/fs/proc/mounts.go b/pkg/sentry/fs/proc/mounts.go index 1e62af8c6..fe62b167b 100644 --- a/pkg/sentry/fs/proc/mounts.go +++ b/pkg/sentry/fs/proc/mounts.go @@ -114,7 +114,7 @@ func (mif *mountInfoFile) ReadSeqFileData(ctx context.Context, handle seqfile.Se // (4) Root: the pathname of the directory in the filesystem // which forms the root of this mount. // - // NOTE: This will always be "/" until we implement + // NOTE(b/78135857): This will always be "/" until we implement // bind mounts. fmt.Fprintf(&buf, "/ ") diff --git a/pkg/sentry/fs/proc/net.go b/pkg/sentry/fs/proc/net.go index 55a958f9e..d24b2d370 100644 --- a/pkg/sentry/fs/proc/net.go +++ b/pkg/sentry/fs/proc/net.go @@ -154,7 +154,7 @@ func (n *netDev) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([]se contents[1] = " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" for _, i := range interfaces { - // TODO: Collect stats from each inet.Stack + // TODO(b/71872867): Collect stats from each inet.Stack // implementation (hostinet, epsocket, and rpcinet). // Implements the same format as diff --git a/pkg/sentry/fs/proc/stat.go b/pkg/sentry/fs/proc/stat.go index f2bbef375..18bd8e9b6 100644 --- a/pkg/sentry/fs/proc/stat.go +++ b/pkg/sentry/fs/proc/stat.go @@ -83,7 +83,7 @@ func (s *statData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([] var buf bytes.Buffer - // TODO: We currently export only zero CPU stats. We could + // TODO(b/37226836): We currently export only zero CPU stats. We could // at least provide some aggregate stats. var cpu cpuStats fmt.Fprintf(&buf, "cpu %s\n", cpu) @@ -100,7 +100,7 @@ func (s *statData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([] const numInterrupts = 256 // The Kernel doesn't handle real interrupts, so report all zeroes. - // TODO: We could count page faults as #PF. + // TODO(b/37226836): We could count page faults as #PF. fmt.Fprintf(&buf, "intr 0") // total for i := 0; i < numInterrupts; i++ { fmt.Fprintf(&buf, " 0") @@ -108,22 +108,22 @@ func (s *statData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([] fmt.Fprintf(&buf, "\n") // Total number of context switches. - // TODO: Count this. + // TODO(b/37226836): Count this. fmt.Fprintf(&buf, "ctxt 0\n") // CLOCK_REALTIME timestamp from boot, in seconds. fmt.Fprintf(&buf, "btime %d\n", s.k.Timekeeper().BootTime().Seconds()) // Total number of clones. - // TODO: Count this. + // TODO(b/37226836): Count this. fmt.Fprintf(&buf, "processes 0\n") // Number of runnable tasks. - // TODO: Count this. + // TODO(b/37226836): Count this. fmt.Fprintf(&buf, "procs_running 0\n") // Number of tasks waiting on IO. - // TODO: Count this. + // TODO(b/37226836): Count this. fmt.Fprintf(&buf, "procs_blocked 0\n") // Number of each softirq handled. diff --git a/pkg/sentry/fs/proc/sys_net.go b/pkg/sentry/fs/proc/sys_net.go index 728a46a74..0ce77f04f 100644 --- a/pkg/sentry/fs/proc/sys_net.go +++ b/pkg/sentry/fs/proc/sys_net.go @@ -39,7 +39,7 @@ const ( // tcpMemInode is used to read/write the size of netstack tcp buffers. // -// TODO: If we have multiple proc mounts, concurrent writes can +// TODO(b/121381035): If we have multiple proc mounts, concurrent writes can // leave netstack and the proc files in an inconsistent state. Since we set the // buffer size from these proc files on restore, we may also race and end up in // an inconsistent state on restore. diff --git a/pkg/sentry/fs/proc/task.go b/pkg/sentry/fs/proc/task.go index 0edcdfce2..9f65a8337 100644 --- a/pkg/sentry/fs/proc/task.go +++ b/pkg/sentry/fs/proc/task.go @@ -77,7 +77,7 @@ func newTaskDir(t *kernel.Task, msrc *fs.MountSource, pidns *kernel.PIDNamespace "fd": newFdDir(t, msrc), "fdinfo": newFdInfoDir(t, msrc), "gid_map": newGIDMap(t, msrc), - // FIXME: create the correct io file for threads. + // FIXME(b/123511468): create the correct io file for threads. "io": newIO(t, msrc), "maps": newMaps(t, msrc), "mountinfo": seqfile.NewSeqFileInode(t, &mountInfoFile{t: t}, msrc), @@ -93,7 +93,7 @@ func newTaskDir(t *kernel.Task, msrc *fs.MountSource, pidns *kernel.PIDNamespace contents["task"] = newSubtasks(t, msrc, pidns) } - // TODO: Set EUID/EGID based on dumpability. + // TODO(b/31916171): Set EUID/EGID based on dumpability. d := &taskDir{ Dir: *ramfs.NewDir(t, contents, fs.RootOwner, fs.FilePermsFromMode(0555)), t: t, @@ -245,7 +245,7 @@ func (e *exe) executable() (d *fs.Dirent, err error) { e.t.WithMuLocked(func(t *kernel.Task) { mm := t.MemoryManager() if mm == nil { - // TODO: Check shouldn't allow Readlink once the + // TODO(b/34851096): Check shouldn't allow Readlink once the // Task is zombied. err = syserror.EACCES return @@ -297,7 +297,7 @@ type namespaceSymlink struct { } func newNamespaceSymlink(t *kernel.Task, msrc *fs.MountSource, name string) *fs.Inode { - // TODO: Namespace symlinks should contain the namespace name and the + // TODO(rahat): Namespace symlinks should contain the namespace name and the // inode number for the namespace instance, so for example user:[123456]. We // currently fake the inode number by sticking the symlink inode in its // place. diff --git a/pkg/sentry/fs/proc/version.go b/pkg/sentry/fs/proc/version.go index b6d49d5e9..58e0c793c 100644 --- a/pkg/sentry/fs/proc/version.go +++ b/pkg/sentry/fs/proc/version.go @@ -65,7 +65,7 @@ func (v *versionData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) // Since we don't really want to expose build information to // applications, those fields are omitted. // - // FIXME: Using Version from the init task SyscallTable + // FIXME(mpratt): Using Version from the init task SyscallTable // disregards the different version a task may have (e.g., in a uts // namespace). ver := init.Leader().SyscallTable().Version diff --git a/pkg/sentry/fs/ramfs/dir.go b/pkg/sentry/fs/ramfs/dir.go index 159fd2981..c0400b67d 100644 --- a/pkg/sentry/fs/ramfs/dir.go +++ b/pkg/sentry/fs/ramfs/dir.go @@ -358,7 +358,7 @@ func (d *Dir) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, p _, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) { return d.NewDir(ctx, dir, perms) }) - // TODO: Support updating status times, as those should be + // TODO(nlacasse): Support updating status times, as those should be // updated by links. return err } diff --git a/pkg/sentry/fs/tmpfs/fs.go b/pkg/sentry/fs/tmpfs/fs.go index d0c93028f..8e44421b6 100644 --- a/pkg/sentry/fs/tmpfs/fs.go +++ b/pkg/sentry/fs/tmpfs/fs.go @@ -34,7 +34,7 @@ const ( // GID for the root directory. rootGIDKey = "gid" - // TODO: support a tmpfs size limit. + // TODO(edahlgren/mpratt): support a tmpfs size limit. // size = "size" // Permissions that exceed modeMask will be rejected. diff --git a/pkg/sentry/fs/tmpfs/inode_file.go b/pkg/sentry/fs/tmpfs/inode_file.go index 7c80d711b..4450e1363 100644 --- a/pkg/sentry/fs/tmpfs/inode_file.go +++ b/pkg/sentry/fs/tmpfs/inode_file.go @@ -309,7 +309,7 @@ func (f *fileInodeOperations) read(ctx context.Context, file *fs.File, dst userm // common: getting a return value of 0 from a read syscall is the only way // to detect EOF. // - // TODO: Separate out f.attr.Size and use atomics instead of + // TODO(jamieliu): Separate out f.attr.Size and use atomics instead of // f.dataMu. f.dataMu.RLock() size := f.attr.Size diff --git a/pkg/sentry/fs/tmpfs/tmpfs.go b/pkg/sentry/fs/tmpfs/tmpfs.go index 555692505..5bb4922cb 100644 --- a/pkg/sentry/fs/tmpfs/tmpfs.go +++ b/pkg/sentry/fs/tmpfs/tmpfs.go @@ -32,7 +32,7 @@ import ( var fsInfo = fs.Info{ Type: linux.TMPFS_MAGIC, - // TODO: allow configuring a tmpfs size and enforce it. + // TODO(b/29637826): allow configuring a tmpfs size and enforce it. TotalBlocks: 0, FreeBlocks: 0, } diff --git a/pkg/sentry/fs/tty/dir.go b/pkg/sentry/fs/tty/dir.go index 33b4c6438..f8713471a 100644 --- a/pkg/sentry/fs/tty/dir.go +++ b/pkg/sentry/fs/tty/dir.go @@ -66,7 +66,7 @@ type dirInodeOperations struct { // msrc is the super block this directory is on. // - // TODO: Plumb this through instead of storing it here. + // TODO(chrisko): Plumb this through instead of storing it here. msrc *fs.MountSource // mu protects the fields below. @@ -89,7 +89,7 @@ type dirInodeOperations struct { // next is the next pty index to use. // - // TODO: reuse indices when ptys are closed. + // TODO(b/29356795): reuse indices when ptys are closed. next uint32 } @@ -118,7 +118,7 @@ func newDir(ctx context.Context, m *fs.MountSource) *fs.Inode { // N.B. Linux always uses inode id 1 for the directory. See // fs/devpts/inode.c:devpts_fill_super. // - // TODO: Since ptsDevice must be shared between + // TODO(b/75267214): Since ptsDevice must be shared between // different mounts, we must not assign fixed numbers. InodeID: ptsDevice.NextIno(), BlockSize: usermem.PageSize, diff --git a/pkg/sentry/fs/tty/fs.go b/pkg/sentry/fs/tty/fs.go index 43e0e2a04..a53448c47 100644 --- a/pkg/sentry/fs/tty/fs.go +++ b/pkg/sentry/fs/tty/fs.go @@ -43,7 +43,7 @@ func (*filesystem) Name() string { // AllowUserMount allows users to mount(2) this file system. func (*filesystem) AllowUserMount() bool { - // TODO: Users may mount this once the terminals are in a + // TODO(b/29356795): Users may mount this once the terminals are in a // usable state. return false } diff --git a/pkg/sentry/fs/tty/master.go b/pkg/sentry/fs/tty/master.go index 7c256abb0..e2686a074 100644 --- a/pkg/sentry/fs/tty/master.go +++ b/pkg/sentry/fs/tty/master.go @@ -51,7 +51,7 @@ func newMasterInode(ctx context.Context, d *dirInodeOperations, owner fs.FileOwn // N.B. Linux always uses inode id 2 for ptmx. See // fs/devpts/inode.c:mknod_ptmx. // - // TODO: Since ptsDevice must be shared between + // TODO(b/75267214): Since ptsDevice must be shared between // different mounts, we must not assign fixed numbers. InodeID: ptsDevice.NextIno(), Type: fs.CharacterDevice, @@ -157,7 +157,7 @@ func (mf *masterFileOperations) Ioctl(ctx context.Context, io usermem.IO, args a // of the slave end. return mf.t.ld.setTermios(ctx, io, args) case linux.TCSETSW: - // TODO: This should drain the output queue first. + // TODO(b/29356795): This should drain the output queue first. return mf.t.ld.setTermios(ctx, io, args) case linux.TIOCGPTN: _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), uint32(mf.t.n), usermem.IOOpts{ @@ -165,7 +165,7 @@ func (mf *masterFileOperations) Ioctl(ctx context.Context, io usermem.IO, args a }) return 0, err case linux.TIOCSPTLCK: - // TODO: Implement pty locking. For now just pretend we do. + // TODO(b/29356795): Implement pty locking. For now just pretend we do. return 0, nil case linux.TIOCGWINSZ: return 0, mf.t.ld.windowSize(ctx, io, args) diff --git a/pkg/sentry/fs/tty/slave.go b/pkg/sentry/fs/tty/slave.go index e8368bcdd..ed080ca0f 100644 --- a/pkg/sentry/fs/tty/slave.go +++ b/pkg/sentry/fs/tty/slave.go @@ -56,7 +56,7 @@ func newSlaveInode(ctx context.Context, d *dirInodeOperations, t *Terminal, owne // N.B. Linux always uses inode id = tty index + 3. See // fs/devpts/inode.c:devpts_pty_new. // - // TODO: Since ptsDevice must be shared between + // TODO(b/75267214): Since ptsDevice must be shared between // different mounts, we must not assign fixed numbers. InodeID: ptsDevice.NextIno(), Type: fs.CharacterDevice, @@ -137,7 +137,7 @@ func (sf *slaveFileOperations) Ioctl(ctx context.Context, io usermem.IO, args ar case linux.TCSETS: return sf.si.t.ld.setTermios(ctx, io, args) case linux.TCSETSW: - // TODO: This should drain the output queue first. + // TODO(b/29356795): This should drain the output queue first. return sf.si.t.ld.setTermios(ctx, io, args) case linux.TIOCGPTN: _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), uint32(sf.si.t.n), usermem.IOOpts{ @@ -151,7 +151,7 @@ func (sf *slaveFileOperations) Ioctl(ctx context.Context, io usermem.IO, args ar case linux.TIOCSCTTY: // Make the given terminal the controlling terminal of the // calling process. - // TODO: Implement once we have support for job + // TODO(b/129283598): Implement once we have support for job // control. return 0, nil default: diff --git a/pkg/sentry/kernel/auth/credentials.go b/pkg/sentry/kernel/auth/credentials.go index a843b9aab..2055da196 100644 --- a/pkg/sentry/kernel/auth/credentials.go +++ b/pkg/sentry/kernel/auth/credentials.go @@ -125,7 +125,7 @@ func NewUserCredentials(kuid KUID, kgid KGID, extraKGIDs []KGID, capabilities *T creds.EffectiveCaps = capabilities.EffectiveCaps creds.BoundingCaps = capabilities.BoundingCaps creds.InheritableCaps = capabilities.InheritableCaps - // TODO: Support ambient capabilities. + // TODO(nlacasse): Support ambient capabilities. } else { // If no capabilities are specified, grant capabilities consistent with // setresuid + setresgid from NewRootCredentials to the given uid and diff --git a/pkg/sentry/kernel/auth/user_namespace.go b/pkg/sentry/kernel/auth/user_namespace.go index 30957bb9a..159940a69 100644 --- a/pkg/sentry/kernel/auth/user_namespace.go +++ b/pkg/sentry/kernel/auth/user_namespace.go @@ -49,7 +49,7 @@ type UserNamespace struct { gidMapFromParent idMapSet gidMapToParent idMapSet - // TODO: Support disabling setgroups(2). + // TODO(b/27454212): Support disabling setgroups(2). } // NewRootUserNamespace returns a UserNamespace that is appropriate for a diff --git a/pkg/sentry/kernel/pending_signals.go b/pkg/sentry/kernel/pending_signals.go index 373e11772..deff6def9 100644 --- a/pkg/sentry/kernel/pending_signals.go +++ b/pkg/sentry/kernel/pending_signals.go @@ -30,7 +30,7 @@ const ( // rtSignalCap is the maximum number of instances of a given realtime // signal that may be pending. // - // TODO: In Linux, the minimum signal queue size is + // TODO(igudger): In Linux, the minimum signal queue size is // RLIMIT_SIGPENDING, which is by default max_threads/2. rtSignalCap = 32 ) diff --git a/pkg/sentry/kernel/ptrace.go b/pkg/sentry/kernel/ptrace.go index 8d78b2fb3..15f2e2964 100644 --- a/pkg/sentry/kernel/ptrace.go +++ b/pkg/sentry/kernel/ptrace.go @@ -162,7 +162,7 @@ func (t *Task) CanTrace(target *Task, attach bool) bool { if cgid := callerCreds.RealKGID; cgid != targetCreds.RealKGID || cgid != targetCreds.EffectiveKGID || cgid != targetCreds.SavedKGID { return false } - // TODO: dumpability check + // TODO(b/31916171): dumpability check if callerCreds.UserNamespace != targetCreds.UserNamespace { return false } @@ -396,7 +396,7 @@ func (t *Task) ptraceAttach(target *Task, seize bool, opts uintptr) error { if target.stop == (*groupStop)(nil) { target.trapStopPending = true target.endInternalStopLocked() - // TODO: Linux blocks ptrace_attach() until the task has + // TODO(jamieliu): Linux blocks ptrace_attach() until the task has // entered the ptrace-stop (or exited) via JOBCTL_TRAPPING. } target.tg.signalHandlers.mu.Unlock() diff --git a/pkg/sentry/kernel/rseq.go b/pkg/sentry/kernel/rseq.go index 0a954bc16..6d3314e81 100644 --- a/pkg/sentry/kernel/rseq.go +++ b/pkg/sentry/kernel/rseq.go @@ -66,7 +66,7 @@ func (t *Task) SetRSEQCriticalRegion(rscr RSEQCriticalRegion) error { if rscr.CriticalSection.Contains(rscr.Restart) { return syserror.EINVAL } - // TODO: check that rscr.CriticalSection and rscr.Restart are in + // TODO(jamieliu): check that rscr.CriticalSection and rscr.Restart are in // the application address range, for consistency with Linux t.tg.rscr.Store(&rscr) return nil diff --git a/pkg/sentry/kernel/sched/cpuset.go b/pkg/sentry/kernel/sched/cpuset.go index 69aee9127..41ac1067d 100644 --- a/pkg/sentry/kernel/sched/cpuset.go +++ b/pkg/sentry/kernel/sched/cpuset.go @@ -29,7 +29,7 @@ type CPUSet []byte // CPUSetSize returns the size in bytes of a CPUSet that can contain num cpus. func CPUSetSize(num uint) uint { - // NOTE: Applications may expect that the size of a CPUSet in + // NOTE(b/68859821): Applications may expect that the size of a CPUSet in // bytes is always a multiple of sizeof(unsigned long), since this is true // in Linux. Thus we always round up. bytes := (num + bitsPerByte - 1) / bitsPerByte diff --git a/pkg/sentry/kernel/semaphore/semaphore.go b/pkg/sentry/kernel/semaphore/semaphore.go index 29a2eb804..2b7c1a9bc 100644 --- a/pkg/sentry/kernel/semaphore/semaphore.go +++ b/pkg/sentry/kernel/semaphore/semaphore.go @@ -302,7 +302,7 @@ func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Cred return syserror.ERANGE } - // TODO: Clear undo entries in all processes + // TODO(b/29354920): Clear undo entries in all processes sem.value = val sem.pid = pid s.changeTime = ktime.NowFromContext(ctx) @@ -336,7 +336,7 @@ func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credenti for i, val := range vals { sem := &s.sems[i] - // TODO: Clear undo entries in all processes + // TODO(b/29354920): Clear undo entries in all processes sem.value = int16(val) sem.pid = pid sem.wakeWaiters() @@ -481,7 +481,7 @@ func (s *Set) executeOps(ctx context.Context, ops []linux.Sembuf, pid int32) (ch } // All operations succeeded, apply them. - // TODO: handle undo operations. + // TODO(b/29354920): handle undo operations. for i, v := range tmpVals { s.sems[i].value = v s.sems[i].wakeWaiters() diff --git a/pkg/sentry/kernel/shm/shm.go b/pkg/sentry/kernel/shm/shm.go index 349f2a26e..d4812a065 100644 --- a/pkg/sentry/kernel/shm/shm.go +++ b/pkg/sentry/kernel/shm/shm.go @@ -427,7 +427,7 @@ func (s *Shm) AddMapping(ctx context.Context, _ memmap.MappingSpace, _ usermem.A func (s *Shm) RemoveMapping(ctx context.Context, _ memmap.MappingSpace, _ usermem.AddrRange, _ uint64, _ bool) { s.mu.Lock() defer s.mu.Unlock() - // TODO: RemoveMapping may be called during task exit, when ctx + // TODO(b/38173783): RemoveMapping may be called during task exit, when ctx // is context.Background. Gracefully handle missing clocks. Failing to // update the detach time in these cases is ok, since no one can observe the // omission. diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index 7eb99718d..293b21249 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -165,7 +165,7 @@ type Stracer interface { // // The returned private data is passed to SyscallExit. // - // TODO: remove kernel imports from the strace + // TODO(gvisor.dev/issue/155): remove kernel imports from the strace // package so that the type can be used directly. SyscallEnter(t *Task, sysno uintptr, args arch.SyscallArguments, flags uint32) interface{} diff --git a/pkg/sentry/kernel/task_context.go b/pkg/sentry/kernel/task_context.go index 1b4d4cf2f..ac38dd157 100644 --- a/pkg/sentry/kernel/task_context.go +++ b/pkg/sentry/kernel/task_context.go @@ -60,7 +60,7 @@ func (tc *TaskContext) release() { // Nil out pointers so that if the task is saved after release, it doesn't // follow the pointers to possibly now-invalid objects. if tc.MemoryManager != nil { - // TODO + // TODO(b/38173783) tc.MemoryManager.DecUsers(context.Background()) tc.MemoryManager = nil } diff --git a/pkg/sentry/kernel/task_exec.go b/pkg/sentry/kernel/task_exec.go index 9fca90a1c..b49f902a5 100644 --- a/pkg/sentry/kernel/task_exec.go +++ b/pkg/sentry/kernel/task_exec.go @@ -208,7 +208,7 @@ func (r *runSyscallAfterExecStop) execute(t *Task) taskRunState { t.tc = *r.tc t.mu.Unlock() t.unstopVforkParent() - // NOTE: All locks must be dropped prior to calling Activate. + // NOTE(b/30316266): All locks must be dropped prior to calling Activate. t.MemoryManager().Activate() t.ptraceExec(oldTID) diff --git a/pkg/sentry/kernel/task_exit.go b/pkg/sentry/kernel/task_exit.go index 1a0734ab6..a07956208 100644 --- a/pkg/sentry/kernel/task_exit.go +++ b/pkg/sentry/kernel/task_exit.go @@ -339,7 +339,7 @@ func (t *Task) exitChildren() { }, true /* group */) other.signalHandlers.mu.Unlock() } - // TODO: The init process waits for all processes in the + // TODO(b/37722272): The init process waits for all processes in the // namespace to exit before completing its own exit // (kernel/pid_namespace.c:zap_pid_ns_processes()). Stop until all // other tasks in the namespace are dead, except possibly for this @@ -692,7 +692,7 @@ func (t *Task) exitNotificationSignal(sig linux.Signal, receiver *Task) *arch.Si info.Code = arch.CLD_EXITED info.SetStatus(int32(t.exitStatus.Code)) } - // TODO: Set utime, stime. + // TODO(b/72102453): Set utime, stime. return info } diff --git a/pkg/sentry/kernel/task_identity.go b/pkg/sentry/kernel/task_identity.go index e105eba13..6c9608f8d 100644 --- a/pkg/sentry/kernel/task_identity.go +++ b/pkg/sentry/kernel/task_identity.go @@ -421,7 +421,7 @@ func (t *Task) SetKeepCaps(k bool) { // updateCredsForExec updates t.creds to reflect an execve(). // -// NOTE: We currently do not implement privileged executables +// NOTE(b/30815691): We currently do not implement privileged executables // (set-user/group-ID bits and file capabilities). This allows us to make a lot // of simplifying assumptions: // diff --git a/pkg/sentry/kernel/task_run.go b/pkg/sentry/kernel/task_run.go index 6b5fe7165..7115aa967 100644 --- a/pkg/sentry/kernel/task_run.go +++ b/pkg/sentry/kernel/task_run.go @@ -110,7 +110,7 @@ func (t *Task) doStop() { return } t.Deactivate() - // NOTE: t.Activate() must be called without any locks held, so + // NOTE(b/30316266): t.Activate() must be called without any locks held, so // this defer must precede the defer for unlocking the signal mutex. defer t.Activate() t.accountTaskGoroutineEnter(TaskGoroutineStopped) diff --git a/pkg/sentry/kernel/task_signals.go b/pkg/sentry/kernel/task_signals.go index 3a8e61900..7f2e0df72 100644 --- a/pkg/sentry/kernel/task_signals.go +++ b/pkg/sentry/kernel/task_signals.go @@ -509,7 +509,7 @@ func (t *Task) canReceiveSignalLocked(sig linux.Signal) bool { if t.stop != nil { return false } - // - TODO: No special case for when t is also the sending task, + // - TODO(b/38173783): No special case for when t is also the sending task, // because the identity of the sender is unknown. // - Do not choose tasks that have already been interrupted, as they may be // busy handling another signal. @@ -895,7 +895,7 @@ func (t *Task) signalStop(target *Task, code int32, status int32) { sigchld.SetPid(int32(t.tg.pidns.tids[target])) sigchld.SetUid(int32(target.Credentials().RealKUID.In(t.UserNamespace()).OrOverflow())) sigchld.SetStatus(status) - // TODO: Set utime, stime. + // TODO(b/72102453): Set utime, stime. t.sendSignalLocked(sigchld, true /* group */) } } diff --git a/pkg/sentry/kernel/task_stop.go b/pkg/sentry/kernel/task_stop.go index 36846484c..1302cadc1 100644 --- a/pkg/sentry/kernel/task_stop.go +++ b/pkg/sentry/kernel/task_stop.go @@ -69,7 +69,7 @@ import ( // A TaskStop is a condition visible to the task control flow graph that // prevents a task goroutine from running or exiting, i.e. an internal stop. // -// NOTE: Most TaskStops don't contain any data; they're +// NOTE(b/30793614): Most TaskStops don't contain any data; they're // distinguished by their type. The obvious way to implement such a TaskStop // is: // diff --git a/pkg/sentry/loader/loader.go b/pkg/sentry/loader/loader.go index 80ad59dde..79051befa 100644 --- a/pkg/sentry/loader/loader.go +++ b/pkg/sentry/loader/loader.go @@ -70,7 +70,7 @@ func openPath(ctx context.Context, mm *fs.MountNamespace, root, wd *fs.Dirent, m defer d.DecRef() perms := fs.PermMask{ - // TODO: Linux requires only execute + // TODO(gvisor.dev/issue/160): Linux requires only execute // permission, not read. However, our backing filesystems may // prevent us from reading the file without read permission. // diff --git a/pkg/sentry/loader/vdso.go b/pkg/sentry/loader/vdso.go index 18b7e90d8..8c196df84 100644 --- a/pkg/sentry/loader/vdso.go +++ b/pkg/sentry/loader/vdso.go @@ -194,7 +194,7 @@ func validateVDSO(ctx context.Context, f *fs.File, size uint64) (elfInfo, error) // VDSO describes a VDSO. // -// NOTE: to support multiple architectures or operating systems, this +// NOTE(mpratt): to support multiple architectures or operating systems, this // would need to contain a VDSO for each. // // +stateify savable @@ -262,7 +262,7 @@ func PrepareVDSO(mfp pgalloc.MemoryFileProvider) (*VDSO, error) { return &VDSO{ ParamPage: mm.NewSpecialMappable("[vvar]", mfp, paramPage), - // TODO: Don't advertise the VDSO, as + // TODO(gvisor.dev/issue/157): Don't advertise the VDSO, as // some applications may not be able to handle multiple [vdso] // hints. vdso: mm.NewSpecialMappable("", mfp, vdso), @@ -279,7 +279,7 @@ func PrepareVDSO(mfp pgalloc.MemoryFileProvider) (*VDSO, error) { // kernel simply directly maps the entire file into process memory, with very // little real ELF parsing. // -// NOTE: This means that userspace can, and unfortunately does, +// NOTE(b/25323870): This means that userspace can, and unfortunately does, // depend on parts of the ELF that would normally not be mapped. To maintain // compatibility with such binaries, we load the VDSO much like Linux. // diff --git a/pkg/sentry/memmap/memmap.go b/pkg/sentry/memmap/memmap.go index 1ef1f0dd8..3f6f7ebd0 100644 --- a/pkg/sentry/memmap/memmap.go +++ b/pkg/sentry/memmap/memmap.go @@ -356,6 +356,6 @@ type MMapOpts struct { // Hint is the name used for the mapping in /proc/[pid]/maps. If Hint is // empty, MappingIdentity.MappedName() will be used instead. // - // TODO: Replace entirely with MappingIdentity? + // TODO(jamieliu): Replace entirely with MappingIdentity? Hint string } diff --git a/pkg/sentry/mm/aio_context.go b/pkg/sentry/mm/aio_context.go index f7ff06de0..7075792e0 100644 --- a/pkg/sentry/mm/aio_context.go +++ b/pkg/sentry/mm/aio_context.go @@ -331,7 +331,7 @@ func (mm *MemoryManager) NewAIOContext(ctx context.Context, events uint32) (uint Length: aioRingBufferSize, MappingIdentity: m, Mappable: m, - // TODO: Linux does "do_mmap_pgoff(..., PROT_READ | + // TODO(fvoznika): Linux does "do_mmap_pgoff(..., PROT_READ | // PROT_WRITE, ...)" in fs/aio.c:aio_setup_ring(); why do we make this // mapping read-only? Perms: usermem.Read, diff --git a/pkg/sentry/mm/procfs.go b/pkg/sentry/mm/procfs.go index 0c4b8895d..7cdbf6e25 100644 --- a/pkg/sentry/mm/procfs.go +++ b/pkg/sentry/mm/procfs.go @@ -69,7 +69,7 @@ func (mm *MemoryManager) ReadMapsSeqFileData(ctx context.Context, handle seqfile start = *handle.(*usermem.Addr) } for vseg := mm.vmas.LowerBoundSegment(start); vseg.Ok(); vseg = vseg.NextSegment() { - // FIXME: If we use a usermem.Addr for the handle, we get + // FIXME(b/30793614): If we use a usermem.Addr for the handle, we get // "panic: autosave error: type usermem.Addr is not registered". vmaAddr := vseg.End() data = append(data, seqfile.SeqData{ @@ -88,7 +88,7 @@ func (mm *MemoryManager) ReadMapsSeqFileData(ctx context.Context, handle seqfile // // Artifically adjust the seqfile handle so we only output vsyscall entry once. if start != vsyscallEnd { - // FIXME: Can't get a pointer to constant vsyscallEnd. + // FIXME(b/30793614): Can't get a pointer to constant vsyscallEnd. vmaAddr := vsyscallEnd data = append(data, seqfile.SeqData{ Buf: []byte(vsyscallMapsEntry), @@ -134,7 +134,7 @@ func (mm *MemoryManager) appendVMAMapsEntryLocked(ctx context.Context, vseg vmaI if vma.hint != "" { s = vma.hint } else if vma.id != nil { - // FIXME: We are holding mm.mappingMu here, which is + // FIXME(jamieliu): We are holding mm.mappingMu here, which is // consistent with Linux's holding mmap_sem in // fs/proc/task_mmu.c:show_map_vma() => fs/seq_file.c:seq_file_path(). // However, it's not clear that fs.File.MappedName() is actually @@ -162,7 +162,7 @@ func (mm *MemoryManager) ReadSmapsSeqFileData(ctx context.Context, handle seqfil start = *handle.(*usermem.Addr) } for vseg := mm.vmas.LowerBoundSegment(start); vseg.Ok(); vseg = vseg.NextSegment() { - // FIXME: If we use a usermem.Addr for the handle, we get + // FIXME(b/30793614): If we use a usermem.Addr for the handle, we get // "panic: autosave error: type usermem.Addr is not registered". vmaAddr := vseg.End() data = append(data, seqfile.SeqData{ @@ -174,7 +174,7 @@ func (mm *MemoryManager) ReadSmapsSeqFileData(ctx context.Context, handle seqfil // We always emulate vsyscall, so advertise it here. See // ReadMapsSeqFileData for additional commentary. if start != vsyscallEnd { - // FIXME: Can't get a pointer to constant vsyscallEnd. + // FIXME(b/30793614): Can't get a pointer to constant vsyscallEnd. vmaAddr := vsyscallEnd data = append(data, seqfile.SeqData{ Buf: []byte(vsyscallSmapsEntry), diff --git a/pkg/sentry/mm/special_mappable.go b/pkg/sentry/mm/special_mappable.go index cfbf7a104..3b5161998 100644 --- a/pkg/sentry/mm/special_mappable.go +++ b/pkg/sentry/mm/special_mappable.go @@ -136,7 +136,7 @@ func (m *SpecialMappable) Length() uint64 { // NewSharedAnonMappable returns a SpecialMappable that implements the // semantics of mmap(MAP_SHARED|MAP_ANONYMOUS) and mappings of /dev/zero. // -// TODO: The use of SpecialMappable is a lazy code reuse hack. Linux +// TODO(jamieliu): The use of SpecialMappable is a lazy code reuse hack. Linux // uses an ephemeral file created by mm/shmem.c:shmem_zero_setup(); we should // do the same to get non-zero device and inode IDs. func NewSharedAnonMappable(length uint64, mfp pgalloc.MemoryFileProvider) (*SpecialMappable, error) { diff --git a/pkg/sentry/mm/syscalls.go b/pkg/sentry/mm/syscalls.go index cc7eb76d2..7b675b9b5 100644 --- a/pkg/sentry/mm/syscalls.go +++ b/pkg/sentry/mm/syscalls.go @@ -137,7 +137,7 @@ func (mm *MemoryManager) MMap(ctx context.Context, opts memmap.MMapOpts) (userme return 0, err } - // TODO: In Linux, VM_LOCKONFAULT (which may be set on the new + // TODO(jamieliu): In Linux, VM_LOCKONFAULT (which may be set on the new // vma by mlockall(MCL_FUTURE|MCL_ONFAULT) => mm_struct::def_flags) appears // to effectively disable MAP_POPULATE by unsetting FOLL_POPULATE in // mm/util.c:vm_mmap_pgoff() => mm/gup.c:__mm_populate() => @@ -148,7 +148,7 @@ func (mm *MemoryManager) MMap(ctx context.Context, opts memmap.MMapOpts) (userme mm.populateVMAAndUnlock(ctx, vseg, ar, true) case opts.Mappable == nil && length <= privateAllocUnit: - // NOTE: Get pmas and map eagerly in the hope + // NOTE(b/63077076, b/63360184): Get pmas and map eagerly in the hope // that doing so will save on future page faults. We only do this for // anonymous mappings, since otherwise the cost of // memmap.Mappable.Translate is unknown; and only for small mappings, @@ -698,7 +698,7 @@ func (mm *MemoryManager) Brk(ctx context.Context, addr usermem.Addr) (usermem.Ad return mm.brk.End, syserror.EINVAL } - // TODO: This enforces RLIMIT_DATA, but is + // TODO(gvisor.dev/issue/156): This enforces RLIMIT_DATA, but is // slightly more permissive than the usual data limit. In particular, // this only limits the size of the heap; a true RLIMIT_DATA limits the // size of heap + data + bss. The segment sizes need to be plumbed from diff --git a/pkg/sentry/mm/vma.go b/pkg/sentry/mm/vma.go index e9c9a80ea..931995254 100644 --- a/pkg/sentry/mm/vma.go +++ b/pkg/sentry/mm/vma.go @@ -274,7 +274,7 @@ func (mm *MemoryManager) getVMAsLocked(ctx context.Context, ar usermem.AddrRange // Loop invariants: vgap = vseg.PrevGap(); addr < vseg.End(). vma := vseg.ValuePtr() if addr < vseg.Start() { - // TODO: Implement vma.growsDown here. + // TODO(jamieliu): Implement vma.growsDown here. return vbegin, vgap, syserror.EFAULT } diff --git a/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go b/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go index c0a0af92d..d0f6bb225 100644 --- a/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go @@ -62,7 +62,7 @@ func updateSystemValues(fd int) error { // Calculate whether guestPCID is supported. // - // FIXME: These should go through the much more pleasant + // FIXME(ascannell): These should go through the much more pleasant // cpuid package interfaces, once a way to accept raw kvm CPUID entries // is plumbed (or some rough equivalent). for i := 0; i < int(cpuidSupported.nr); i++ { diff --git a/pkg/sentry/platform/platform.go b/pkg/sentry/platform/platform.go index d1c9458ea..0e48417b9 100644 --- a/pkg/sentry/platform/platform.go +++ b/pkg/sentry/platform/platform.go @@ -181,7 +181,7 @@ var ( // this signal both to Contexts and to the sentry itself, under the assumption // that they originate from races with Context.Interrupt(). // -// NOTE: The Go runtime only guarantees that a small subset +// NOTE(b/23420492): The Go runtime only guarantees that a small subset // of signals will be always be unblocked on all threads, one of which // is SIGCHLD. const SignalInterrupt = linux.SIGCHLD diff --git a/pkg/sentry/platform/ptrace/subprocess.go b/pkg/sentry/platform/ptrace/subprocess.go index 82f125073..2a5d699ec 100644 --- a/pkg/sentry/platform/ptrace/subprocess.go +++ b/pkg/sentry/platform/ptrace/subprocess.go @@ -79,7 +79,7 @@ func (tp *threadPool) lookupOrCreate(currentTID int32, newThread func() *thread) // Before creating a new thread, see if we can find a thread // whose system tid has disappeared. // - // TODO: Other parts of this package depend on + // TODO(b/77216482): Other parts of this package depend on // threads never exiting. for origTID, t := range tp.threads { // Signal zero is an easy existence check. diff --git a/pkg/sentry/platform/ring0/x86.go b/pkg/sentry/platform/ring0/x86.go index 7c88010d8..4c6daec22 100644 --- a/pkg/sentry/platform/ring0/x86.go +++ b/pkg/sentry/platform/ring0/x86.go @@ -116,7 +116,7 @@ const ( // // Note that sign-extension semantics apply to the highest order bit. // -// FIXME: This should use the cpuid passed to Init. +// FIXME(b/69382326): This should use the cpuid passed to Init. func VirtualAddressBits() uint32 { ax, _, _, _ := cpuid.HostID(0x80000008, 0) return (ax >> 8) & 0xff @@ -124,7 +124,7 @@ func VirtualAddressBits() uint32 { // PhysicalAddressBits returns the number of bits available for physical addresses. // -// FIXME: This should use the cpuid passed to Init. +// FIXME(b/69382326): This should use the cpuid passed to Init. func PhysicalAddressBits() uint32 { ax, _, _, _ := cpuid.HostID(0x80000008, 0) return ax & 0xff diff --git a/pkg/sentry/sighandling/sighandling.go b/pkg/sentry/sighandling/sighandling.go index 6b5d5f993..571245ce5 100644 --- a/pkg/sentry/sighandling/sighandling.go +++ b/pkg/sentry/sighandling/sighandling.go @@ -86,7 +86,7 @@ func handleSignals(sigchans []chan os.Signal, handler func(linux.Signal), start, // // Otherwise ignore the signal. // - // TODO: Drop in Go 1.12, which uses tgkill + // TODO(b/114489875): Drop in Go 1.12, which uses tgkill // in runtime.raise. switch signal { case linux.SIGHUP, linux.SIGINT, linux.SIGTERM: diff --git a/pkg/sentry/sighandling/sighandling_unsafe.go b/pkg/sentry/sighandling/sighandling_unsafe.go index 5913d47a8..db6e71487 100644 --- a/pkg/sentry/sighandling/sighandling_unsafe.go +++ b/pkg/sentry/sighandling/sighandling_unsafe.go @@ -23,7 +23,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/abi/linux" ) -// TODO: Move to pkg/abi/linux along with definitions in +// TODO(b/34161764): Move to pkg/abi/linux along with definitions in // pkg/sentry/arch. type sigaction struct { handler uintptr diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go index 23138d874..768fa0dfa 100644 --- a/pkg/sentry/socket/epsocket/epsocket.go +++ b/pkg/sentry/socket/epsocket/epsocket.go @@ -608,7 +608,7 @@ func (s *SocketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error { // GetSockOpt implements the linux syscall getsockopt(2) for sockets backed by // tcpip.Endpoint. func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name, outLen int) (interface{}, *syserr.Error) { - // TODO: Unlike other socket options, SO_TIMESTAMP is + // TODO(b/78348848): Unlike other socket options, SO_TIMESTAMP is // implemented specifically for epsocket.SocketOperations rather than // commonEndpoint. commonEndpoint should be extended to support socket // options where the implementation is not shared, as unix sockets need @@ -658,7 +658,7 @@ func GetSockOpt(t *kernel.Task, s socket.Socket, ep commonEndpoint, family int, // getSockOptSocket implements GetSockOpt when level is SOL_SOCKET. func getSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, family int, skType transport.SockType, name, outLen int) (interface{}, *syserr.Error) { - // TODO: Stop rejecting short optLen values in getsockopt. + // TODO(b/124056281): Stop rejecting short optLen values in getsockopt. switch name { case linux.SO_TYPE: if outLen < sizeOfInt32 { @@ -789,7 +789,7 @@ func getSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, family return linux.Linger{}, nil case linux.SO_SNDTIMEO: - // TODO: Linux allows shorter lengths for partial results. + // TODO(igudger): Linux allows shorter lengths for partial results. if outLen < linux.SizeOfTimeval { return nil, syserr.ErrInvalidArgument } @@ -797,7 +797,7 @@ func getSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, family return linux.NsecToTimeval(s.SendTimeout()), nil case linux.SO_RCVTIMEO: - // TODO: Linux allows shorter lengths for partial results. + // TODO(igudger): Linux allows shorter lengths for partial results. if outLen < linux.SizeOfTimeval { return nil, syserr.ErrInvalidArgument } @@ -894,7 +894,7 @@ func getSockOptTCP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfa return nil, syserr.TranslateNetstackError(err) } - // TODO: Translate fields once they are added to + // TODO(b/64800844): Translate fields once they are added to // tcpip.TCPInfoOption. info := linux.TCPInfo{} @@ -995,7 +995,7 @@ func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfac // SetSockOpt implements the linux syscall setsockopt(2) for sockets backed by // tcpip.Endpoint. func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVal []byte) *syserr.Error { - // TODO: Unlike other socket options, SO_TIMESTAMP is + // TODO(b/78348848): Unlike other socket options, SO_TIMESTAMP is // implemented specifically for epsocket.SocketOperations rather than // commonEndpoint. commonEndpoint should be extended to support socket // options where the implementation is not shared, as unix sockets need @@ -1338,7 +1338,7 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.AddMembershipOption{ NIC: tcpip.NICID(req.InterfaceIndex), - // TODO: Change AddMembership to use the standard + // TODO(igudger): Change AddMembership to use the standard // any address representation. InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]), MulticastAddr: tcpip.Address(req.MulticastAddr[:]), @@ -1352,7 +1352,7 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.RemoveMembershipOption{ NIC: tcpip.NICID(req.InterfaceIndex), - // TODO: Change DropMembership to use the standard + // TODO(igudger): Change DropMembership to use the standard // any address representation. InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]), MulticastAddr: tcpip.Address(req.MulticastAddr[:]), @@ -1380,7 +1380,7 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s )) case linux.MCAST_JOIN_GROUP: - // FIXME: Implement MCAST_JOIN_GROUP. + // FIXME(b/124219304): Implement MCAST_JOIN_GROUP. t.Kernel().EmitUnimplementedEvent(t) return syserr.ErrInvalidArgument @@ -1695,7 +1695,7 @@ func (s *SocketOperations) coalescingRead(ctx context.Context, dst usermem.IOSeq // nonBlockingRead issues a non-blocking read. // -// TODO: Support timestamps for stream sockets. +// TODO(b/78348848): Support timestamps for stream sockets. func (s *SocketOperations) nonBlockingRead(ctx context.Context, dst usermem.IOSequence, peek, trunc, senderRequested bool) (int, int, interface{}, uint32, socket.ControlMessages, *syserr.Error) { isPacket := s.isPacketBased() @@ -1762,7 +1762,7 @@ func (s *SocketOperations) nonBlockingRead(ctx context.Context, dst usermem.IOSe dst = dst.DropFirst(n) num, err := dst.CopyOutFrom(ctx, safemem.FromVecReaderFunc{func(dsts [][]byte) (int64, error) { n, _, err := s.Endpoint.Peek(dsts) - // TODO: Handle peek timestamp. + // TODO(b/78348848): Handle peek timestamp. if err != nil { return int64(n), syserr.TranslateNetstackError(err).ToError() } @@ -1963,7 +1963,7 @@ func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to [] func (s *SocketOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { // SIOCGSTAMP is implemented by epsocket rather than all commonEndpoint // sockets. - // TODO: Add a commonEndpoint method to support SIOCGSTAMP. + // TODO(b/78348848): Add a commonEndpoint method to support SIOCGSTAMP. if int(args[1].Int()) == syscall.SIOCGSTAMP { s.readMu.Lock() defer s.readMu.Unlock() @@ -2153,19 +2153,19 @@ func interfaceIoctl(ctx context.Context, io usermem.IO, arg int, ifr *linux.IFRe case syscall.SIOCGIFMAP: // Gets the hardware parameters of the device. - // TODO: Implement. + // TODO(b/71872867): Implement. case syscall.SIOCGIFTXQLEN: // Gets the transmit queue length of the device. - // TODO: Implement. + // TODO(b/71872867): Implement. case syscall.SIOCGIFDSTADDR: // Gets the destination address of a point-to-point device. - // TODO: Implement. + // TODO(b/71872867): Implement. case syscall.SIOCGIFBRDADDR: // Gets the broadcast address of a device. - // TODO: Implement. + // TODO(b/71872867): Implement. case syscall.SIOCGIFNETMASK: // Gets the network mask of a device. diff --git a/pkg/sentry/socket/epsocket/save_restore.go b/pkg/sentry/socket/epsocket/save_restore.go index 34d9a7cf0..f19afb6c0 100644 --- a/pkg/sentry/socket/epsocket/save_restore.go +++ b/pkg/sentry/socket/epsocket/save_restore.go @@ -20,7 +20,7 @@ import ( // afterLoad is invoked by stateify. func (s *Stack) afterLoad() { - s.Stack = stack.StackFromEnv // FIXME + s.Stack = stack.StackFromEnv // FIXME(b/36201077) if s.Stack == nil { panic("can't restore without netstack/tcpip/stack.Stack") } diff --git a/pkg/sentry/socket/epsocket/stack.go b/pkg/sentry/socket/epsocket/stack.go index c0081c819..37c48f4bc 100644 --- a/pkg/sentry/socket/epsocket/stack.go +++ b/pkg/sentry/socket/epsocket/stack.go @@ -77,7 +77,7 @@ func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr { Family: family, PrefixLen: uint8(len(a.Address) * 8), Addr: []byte(a.Address), - // TODO: Other fields. + // TODO(b/68878065): Other fields. }) } nicAddrs[int32(id)] = addrs diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go index c4848b313..49349074f 100644 --- a/pkg/sentry/socket/hostinet/socket.go +++ b/pkg/sentry/socket/hostinet/socket.go @@ -348,7 +348,7 @@ func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt [ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (int, int, interface{}, uint32, socket.ControlMessages, *syserr.Error) { // Whitelist flags. // - // FIXME: We can't support MSG_ERRQUEUE because it uses ancillary + // FIXME(jamieliu): We can't support MSG_ERRQUEUE because it uses ancillary // messages that netstack/tcpip/transport/unix doesn't understand. Kill the // Socket interface's dependence on netstack. if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_PEEK|syscall.MSG_TRUNC) != 0 { diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index 7e70b09b2..e414b829b 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -110,7 +110,7 @@ func (p *Protocol) dumpLinks(ctx context.Context, hdr linux.NetlinkMessageHeader m.PutAttr(linux.IFLA_ADDRESS, mac) m.PutAttr(linux.IFLA_BROADCAST, brd) - // TODO: There are many more attributes. + // TODO(b/68878065): There are many more attributes. } return nil @@ -122,7 +122,7 @@ func (p *Protocol) dumpAddrs(ctx context.Context, hdr linux.NetlinkMessageHeader // netlink header and 1 byte protocol family common to all // NETLINK_ROUTE requests. // - // TODO: Filter output by passed protocol family. + // TODO(b/68878065): Filter output by passed protocol family. // The RTM_GETADDR dump response is a set of RTM_NEWADDR messages each // containing an InterfaceAddrMessage followed by a set of netlink @@ -151,7 +151,7 @@ func (p *Protocol) dumpAddrs(ctx context.Context, hdr linux.NetlinkMessageHeader m.PutAttr(linux.IFA_ADDRESS, []byte(a.Addr)) - // TODO: There are many more attributes. + // TODO(b/68878065): There are many more attributes. } } @@ -175,7 +175,7 @@ func (p *Protocol) ProcessMessage(ctx context.Context, hdr linux.NetlinkMessageH } } - // TODO: Only the dump variant of the types below are + // TODO(b/68878065): Only the dump variant of the types below are // supported. if hdr.Flags&linux.NLM_F_DUMP != linux.NLM_F_DUMP { return syserr.ErrNotSupported diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go index 0fe9b39b6..a34f9d3ca 100644 --- a/pkg/sentry/socket/netlink/socket.go +++ b/pkg/sentry/socket/netlink/socket.go @@ -168,7 +168,7 @@ func (s *Socket) EventUnregister(e *waiter.Entry) { // Ioctl implements fs.FileOperations.Ioctl. func (s *Socket) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { - // TODO: no ioctls supported. + // TODO(b/68878065): no ioctls supported. return 0, syserror.ENOTTY } @@ -319,7 +319,7 @@ func (s *Socket) GetSockOpt(t *kernel.Task, level int, name int, outLen int) (in t.Kernel().EmitUnimplementedEvent(t) } } - // TODO: other sockopts are not supported. + // TODO(b/68878065): other sockopts are not supported. return nil, syserr.ErrProtocolNotAvailable } @@ -369,7 +369,7 @@ func (s *Socket) SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *sy } } - // TODO: other sockopts are not supported. + // TODO(b/68878065): other sockopts are not supported. return syserr.ErrProtocolNotAvailable } @@ -389,7 +389,7 @@ func (s *Socket) GetSockName(t *kernel.Task) (interface{}, uint32, *syserr.Error func (s *Socket) GetPeerName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { sa := linux.SockAddrNetlink{ Family: linux.AF_NETLINK, - // TODO: Support non-kernel peers. For now the peer + // TODO(b/68878065): Support non-kernel peers. For now the peer // must be the kernel. PortID: 0, } @@ -540,7 +540,7 @@ func (s *Socket) processMessages(ctx context.Context, buf []byte) *syserr.Error continue } - // TODO: ACKs not supported yet. + // TODO(b/68877377): ACKs not supported yet. if hdr.Flags&linux.NLM_F_ACK == linux.NLM_F_ACK { return syserr.ErrNotSupported } diff --git a/pkg/sentry/socket/rpcinet/conn/conn.go b/pkg/sentry/socket/rpcinet/conn/conn.go index 9c749b888..64106c4b5 100644 --- a/pkg/sentry/socket/rpcinet/conn/conn.go +++ b/pkg/sentry/socket/rpcinet/conn/conn.go @@ -50,7 +50,7 @@ type RPCConnection struct { // NewRPCConnection initializes a RPC connection to a socket gofer. func NewRPCConnection(s *unet.Socket) *RPCConnection { conn := &RPCConnection{socket: s, requests: map[uint64]request{}} - go func() { // S/R-FIXME + go func() { // S/R-FIXME(b/77962828) var nums [16]byte for { for n := 0; n < len(nums); { diff --git a/pkg/sentry/socket/rpcinet/notifier/notifier.go b/pkg/sentry/socket/rpcinet/notifier/notifier.go index d9bda78b0..f06d12231 100644 --- a/pkg/sentry/socket/rpcinet/notifier/notifier.go +++ b/pkg/sentry/socket/rpcinet/notifier/notifier.go @@ -64,7 +64,7 @@ func NewRPCNotifier(cn *conn.RPCConnection) (*Notifier, error) { fdMap: make(map[uint32]*fdInfo), } - go w.waitAndNotify() // S/R-FIXME + go w.waitAndNotify() // S/R-FIXME(b/77962828) return w, nil } @@ -166,7 +166,7 @@ func (n *Notifier) waitAndNotify() error { res := n.rpcConn.Request(id).Result.(*pb.SyscallResponse_EpollWait).EpollWait.Result if e, ok := res.(*pb.EpollWaitResponse_ErrorNumber); ok { err := syscall.Errno(e.ErrorNumber) - // NOTE: I don't think epoll_wait can return EAGAIN but I'm being + // NOTE(magi): I don't think epoll_wait can return EAGAIN but I'm being // conseratively careful here since exiting the notification thread // would be really bad. if err == syscall.EINTR || err == syscall.EAGAIN { diff --git a/pkg/sentry/socket/rpcinet/socket.go b/pkg/sentry/socket/rpcinet/socket.go index 3418a6d75..cf8f69efb 100644 --- a/pkg/sentry/socket/rpcinet/socket.go +++ b/pkg/sentry/socket/rpcinet/socket.go @@ -288,7 +288,7 @@ func (s *socketOperations) Accept(t *kernel.Task, peerRequested bool, flags int, if blocking && se == syserr.ErrTryAgain { // Register for notifications. e, ch := waiter.NewChannelEntry(nil) - // FIXME: This waiter.EventHUp is a partial + // FIXME(b/119878986): This waiter.EventHUp is a partial // measure, need to figure out how to translate linux events to // internal events. s.EventRegister(&e, waiter.EventIn|waiter.EventHUp) @@ -370,7 +370,7 @@ func (s *socketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error { // We save the shutdown state because of strange differences on linux // related to recvs on blocking vs. non-blocking sockets after a SHUT_RD. // We need to emulate that behavior on the blocking side. - // TODO: There is a possible race that can exist with loopback, + // TODO(b/120096741): There is a possible race that can exist with loopback, // where data could possibly be lost. s.setShutdownFlags(how) @@ -771,7 +771,7 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to [] return 0, syserr.FromError(err) } - // TODO: this needs to change to map directly to a SendMsg syscall + // TODO(bgeffon): this needs to change to map directly to a SendMsg syscall // in the RPC. totalWritten := 0 n, err := rpcSendMsg(t, &pb.SyscallRequest_Sendmsg{&pb.SendmsgRequest{ diff --git a/pkg/sentry/socket/rpcinet/syscall_rpc.proto b/pkg/sentry/socket/rpcinet/syscall_rpc.proto index c056e4c9d..9586f5923 100644 --- a/pkg/sentry/socket/rpcinet/syscall_rpc.proto +++ b/pkg/sentry/socket/rpcinet/syscall_rpc.proto @@ -3,7 +3,7 @@ syntax = "proto3"; // package syscall_rpc is a set of networking related system calls that can be // forwarded to a socket gofer. // -// TODO: Document individual RPCs. +// TODO(b/77963526): Document individual RPCs. package syscall_rpc; message SendmsgRequest { diff --git a/pkg/sentry/strace/strace.go b/pkg/sentry/strace/strace.go index a6d870b44..434a200d9 100644 --- a/pkg/sentry/strace/strace.go +++ b/pkg/sentry/strace/strace.go @@ -722,7 +722,7 @@ func (s SyscallMap) Name(sysno uintptr) string { // N.B. This is not in an init function because we can't be sure all syscall // tables are registered with the kernel when init runs. // -// TODO: remove kernel package dependencies from this +// TODO(gvisor.dev/issue/155): remove kernel package dependencies from this // package and have the kernel package self-initialize all syscall tables. func Initialize() { for _, table := range kernel.SyscallTables() { diff --git a/pkg/sentry/syscalls/linux/error.go b/pkg/sentry/syscalls/linux/error.go index 8759e5e32..304a12dde 100644 --- a/pkg/sentry/syscalls/linux/error.go +++ b/pkg/sentry/syscalls/linux/error.go @@ -89,7 +89,7 @@ func handleIOError(t *kernel.Task, partialResult bool, err, intr error, op strin // side is gone. The partial write is returned. EPIPE will be // returned on the next call. // - // TODO: In some cases SIGPIPE should + // TODO(gvisor.dev/issue/161): In some cases SIGPIPE should // also be sent to the application. return nil case syserror.ErrWouldBlock: diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index be793ca11..b9b4ccbd1 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -143,10 +143,10 @@ var AMD64 = &kernel.SyscallTable{ 65: Semop, 66: Semctl, 67: Shmdt, - // 68: @Syscall(Msgget), TODO - // 69: @Syscall(Msgsnd), TODO - // 70: @Syscall(Msgrcv), TODO - // 71: @Syscall(Msgctl), TODO + // 68: @Syscall(Msgget), TODO(b/29354921) + // 69: @Syscall(Msgsnd), TODO(b/29354921) + // 70: @Syscall(Msgrcv), TODO(b/29354921) + // 71: @Syscall(Msgctl), TODO(b/29354921) 72: Fcntl, 73: Flock, 74: Fsync, @@ -197,8 +197,8 @@ var AMD64 = &kernel.SyscallTable{ 119: Setresgid, 120: Getresgid, 121: Getpgid, - // 122: @Syscall(Setfsuid), TODO - // 123: @Syscall(Setfsgid), TODO + // 122: @Syscall(Setfsuid), TODO(b/112851702) + // 123: @Syscall(Setfsgid), TODO(b/112851702) 124: Getsid, 125: Capget, 126: Capset, @@ -217,7 +217,7 @@ var AMD64 = &kernel.SyscallTable{ 136: syscalls.ErrorWithEvent(syscall.ENOSYS), 137: Statfs, 138: Fstatfs, - // 139: @Syscall(Sysfs), TODO + // 139: @Syscall(Sysfs), TODO(gvisor.dev/issue/165) 140: Getpriority, 141: Setpriority, // @Syscall(SchedSetparam, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_nice; ENOSYS otherwise) @@ -291,7 +291,7 @@ var AMD64 = &kernel.SyscallTable{ // @Syscall(Security, note:Not implemented in Linux) 185: syscalls.Error(syscall.ENOSYS), 186: Gettid, - 187: nil, // @Syscall(Readahead), TODO + 187: nil, // @Syscall(Readahead), TODO(b/29351341) // @Syscall(Setxattr, returns:ENOTSUP, note:Requires filesystem support) 188: syscalls.ErrorWithEvent(syscall.ENOTSUP), // @Syscall(Lsetxattr, returns:ENOTSUP, note:Requires filesystem support) @@ -342,7 +342,7 @@ var AMD64 = &kernel.SyscallTable{ 217: Getdents64, 218: SetTidAddress, 219: RestartSyscall, - // 220: @Syscall(Semtimedop), TODO + // 220: @Syscall(Semtimedop), TODO(b/29354920) 221: Fadvise64, 222: TimerCreate, 223: TimerSettime, @@ -360,16 +360,16 @@ var AMD64 = &kernel.SyscallTable{ 235: Utimes, // @Syscall(Vserver, note:Not implemented by Linux) 236: syscalls.Error(syscall.ENOSYS), // Vserver, not implemented by Linux - // @Syscall(Mbind, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_nice; ENOSYS otherwise), TODO + // @Syscall(Mbind, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_nice; ENOSYS otherwise), TODO(b/117792295) 237: syscalls.CapError(linux.CAP_SYS_NICE), // may require cap_sys_nice 238: SetMempolicy, 239: GetMempolicy, - // 240: @Syscall(MqOpen), TODO - // 241: @Syscall(MqUnlink), TODO - // 242: @Syscall(MqTimedsend), TODO - // 243: @Syscall(MqTimedreceive), TODO - // 244: @Syscall(MqNotify), TODO - // 245: @Syscall(MqGetsetattr), TODO + // 240: @Syscall(MqOpen), TODO(b/29354921) + // 241: @Syscall(MqUnlink), TODO(b/29354921) + // 242: @Syscall(MqTimedsend), TODO(b/29354921) + // 243: @Syscall(MqTimedreceive), TODO(b/29354921) + // 244: @Syscall(MqNotify), TODO(b/29354921) + // 245: @Syscall(MqGetsetattr), TODO(b/29354921) 246: syscalls.CapError(linux.CAP_SYS_BOOT), // kexec_load, requires cap_sys_boot 247: Waitid, // @Syscall(AddKey, returns:EACCES, note:Not available to user) @@ -407,22 +407,22 @@ var AMD64 = &kernel.SyscallTable{ 273: syscalls.Error(syscall.ENOSYS), // @Syscall(GetRobustList, note:Obsolete) 274: syscalls.Error(syscall.ENOSYS), - // 275: @Syscall(Splice), TODO - // 276: @Syscall(Tee), TODO + // 275: @Syscall(Splice), TODO(b/29354098) + // 276: @Syscall(Tee), TODO(b/29354098) 277: SyncFileRange, - // 278: @Syscall(Vmsplice), TODO + // 278: @Syscall(Vmsplice), TODO(b/29354098) // @Syscall(MovePages, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_nice; ENOSYS otherwise) 279: syscalls.CapError(linux.CAP_SYS_NICE), // requires cap_sys_nice (mostly) 280: Utimensat, 281: EpollPwait, - // 282: @Syscall(Signalfd), TODO + // 282: @Syscall(Signalfd), TODO(b/19846426) 283: TimerfdCreate, 284: Eventfd, 285: Fallocate, 286: TimerfdSettime, 287: TimerfdGettime, 288: Accept4, - // 289: @Syscall(Signalfd4), TODO + // 289: @Syscall(Signalfd4), TODO(b/19846426) 290: Eventfd2, 291: EpollCreate1, 292: Dup3, @@ -447,17 +447,17 @@ var AMD64 = &kernel.SyscallTable{ 305: syscalls.CapError(linux.CAP_SYS_TIME), // requires cap_sys_time 306: Syncfs, 307: SendMMsg, - // 308: @Syscall(Setns), TODO + // 308: @Syscall(Setns), TODO(b/29354995) 309: Getcpu, - // 310: @Syscall(ProcessVmReadv), TODO may require cap_sys_ptrace - // 311: @Syscall(ProcessVmWritev), TODO may require cap_sys_ptrace + // 310: @Syscall(ProcessVmReadv), TODO(gvisor.dev/issue/158) may require cap_sys_ptrace + // 311: @Syscall(ProcessVmWritev), TODO(gvisor.dev/issue/158) may require cap_sys_ptrace // @Syscall(Kcmp, returns:EPERM or ENOSYS, note:Requires cap_sys_ptrace) 312: syscalls.CapError(linux.CAP_SYS_PTRACE), // @Syscall(FinitModule, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_module; ENOSYS otherwise) 313: syscalls.CapError(linux.CAP_SYS_MODULE), - // 314: @Syscall(SchedSetattr), TODO, we have no scheduler - // 315: @Syscall(SchedGetattr), TODO, we have no scheduler - // 316: @Syscall(Renameat2), TODO + // 314: @Syscall(SchedSetattr), TODO(b/118902272), we have no scheduler + // 315: @Syscall(SchedGetattr), TODO(b/118902272), we have no scheduler + // 316: @Syscall(Renameat2), TODO(b/118902772) 317: Seccomp, 318: GetRandom, 319: MemfdCreate, @@ -465,9 +465,9 @@ var AMD64 = &kernel.SyscallTable{ 320: syscalls.CapError(linux.CAP_SYS_BOOT), // @Syscall(Bpf, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_boot; ENOSYS otherwise) 321: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_admin for all commands - // 322: @Syscall(Execveat), TODO - // 323: @Syscall(Userfaultfd), TODO - // 324: @Syscall(Membarrier), TODO + // 322: @Syscall(Execveat), TODO(b/118901836) + // 323: @Syscall(Userfaultfd), TODO(b/118906345) + // 324: @Syscall(Membarrier), TODO(b/118904897) 325: Mlock2, // Syscalls after 325 are "backports" from versions of Linux after 4.4. // 326: @Syscall(CopyFileRange), diff --git a/pkg/sentry/syscalls/linux/sys_aio.go b/pkg/sentry/syscalls/linux/sys_aio.go index 355071131..61c2647bf 100644 --- a/pkg/sentry/syscalls/linux/sys_aio.go +++ b/pkg/sentry/syscalls/linux/sys_aio.go @@ -120,7 +120,7 @@ func IoDestroy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys // Does not exist. return 0, nil, syserror.EINVAL } - // FIXME: Linux blocks until all AIO to the destroyed context is + // FIXME(fvoznika): Linux blocks until all AIO to the destroyed context is // done. return 0, nil, nil } diff --git a/pkg/sentry/syscalls/linux/sys_file.go b/pkg/sentry/syscalls/linux/sys_file.go index 50151f7b6..967464c85 100644 --- a/pkg/sentry/syscalls/linux/sys_file.go +++ b/pkg/sentry/syscalls/linux/sys_file.go @@ -259,7 +259,7 @@ func mknodAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, mode linux.FileM case linux.ModeCharacterDevice: fallthrough case linux.ModeBlockDevice: - // TODO: We don't support creating block or character + // TODO(b/72101894): We don't support creating block or character // devices at the moment. // // When we start supporting block and character devices, we'll @@ -1532,7 +1532,7 @@ func chown(t *kernel.Task, d *fs.Dirent, uid auth.UID, gid auth.GID) error { owner.GID = kgid } - // FIXME: This is racy; the inode's owner may have changed in + // FIXME(b/62949101): This is racy; the inode's owner may have changed in // the meantime. (Linux holds i_mutex while calling // fs/attr.c:notify_change() => inode_operations::setattr => // inode_change_ok().) diff --git a/pkg/sentry/syscalls/linux/sys_mmap.go b/pkg/sentry/syscalls/linux/sys_mmap.go index 8732861e0..805b251b1 100644 --- a/pkg/sentry/syscalls/linux/sys_mmap.go +++ b/pkg/sentry/syscalls/linux/sys_mmap.go @@ -185,7 +185,7 @@ func Madvise(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca case linux.MADV_MERGEABLE, linux.MADV_UNMERGEABLE: fallthrough case linux.MADV_DONTDUMP, linux.MADV_DODUMP: - // TODO: Core dumping isn't implemented, so these are + // TODO(b/72045799): Core dumping isn't implemented, so these are // no-ops. fallthrough case linux.MADV_NORMAL, linux.MADV_RANDOM, linux.MADV_SEQUENTIAL, linux.MADV_WILLNEED: @@ -223,7 +223,7 @@ func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. nodeFlag := flags&linux.MPOL_F_NODE != 0 addrFlag := flags&linux.MPOL_F_ADDR != 0 - // TODO: Once sysfs is implemented, report a single numa node in + // TODO(rahat): Once sysfs is implemented, report a single numa node in // /sys/devices/system/node. if nodemask != 0 && maxnode < 1 { return 0, nil, syserror.EINVAL diff --git a/pkg/sentry/syscalls/linux/sys_read.go b/pkg/sentry/syscalls/linux/sys_read.go index 8105e9b43..50c7d7a74 100644 --- a/pkg/sentry/syscalls/linux/sys_read.go +++ b/pkg/sentry/syscalls/linux/sys_read.go @@ -192,7 +192,7 @@ func Preadv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Preadv2 implements linux syscall preadv2(2). -// TODO: Implement RWF_HIPRI functionality. +// TODO(b/120162627): Implement RWF_HIPRI functionality. func Preadv2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // While the syscall is // preadv2(int fd, struct iovec* iov, int iov_cnt, off_t offset, int flags) diff --git a/pkg/sentry/syscalls/linux/sys_socket.go b/pkg/sentry/syscalls/linux/sys_socket.go index 30ccc3f66..c8748958a 100644 --- a/pkg/sentry/syscalls/linux/sys_socket.go +++ b/pkg/sentry/syscalls/linux/sys_socket.go @@ -317,7 +317,7 @@ func accept(t *kernel.Task, fd kdefs.FD, addr usermem.Addr, addrLen usermem.Addr return 0, syserror.ConvertIntr(e.ToError(), kernel.ERESTARTSYS) } if peerRequested { - // NOTE: Linux does not give you an error if it can't + // NOTE(magi): Linux does not give you an error if it can't // write the data back out so neither do we. if err := writeAddress(t, peer, peerLen, addr, addrLen); err == syscall.EINVAL { return 0, err @@ -735,7 +735,7 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr usermem.Addr, flags i return 0, err } - // FIXME: Pretend we have an empty error queue. + // FIXME(b/63594852): Pretend we have an empty error queue. if flags&linux.MSG_ERRQUEUE != 0 { return 0, syscall.EAGAIN } diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go index 61cafefb9..ddcb5b789 100644 --- a/pkg/sentry/syscalls/linux/sys_thread.go +++ b/pkg/sentry/syscalls/linux/sys_thread.go @@ -350,7 +350,7 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } si.SetPid(int32(wr.TID)) si.SetUid(int32(wr.UID)) - // TODO: convert kernel.ExitStatus to functions and make + // TODO(b/73541790): convert kernel.ExitStatus to functions and make // WaitResult.Status a linux.WaitStatus s := syscall.WaitStatus(wr.Status) switch { diff --git a/pkg/sentry/syscalls/linux/sys_write.go b/pkg/sentry/syscalls/linux/sys_write.go index a5ad7efb2..e405608c4 100644 --- a/pkg/sentry/syscalls/linux/sys_write.go +++ b/pkg/sentry/syscalls/linux/sys_write.go @@ -192,8 +192,8 @@ func Pwritev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Pwritev2 implements linux syscall pwritev2(2). -// TODO: Implement RWF_HIPRI functionality. -// TODO: Implement O_SYNC and D_SYNC functionality. +// TODO(b/120162627): Implement RWF_HIPRI functionality. +// TODO(b/120161091): Implement O_SYNC and D_SYNC functionality. func Pwritev2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // While the syscall is // pwritev2(int fd, struct iovec* iov, int iov_cnt, off_t offset, int flags) diff --git a/pkg/sentry/time/calibrated_clock.go b/pkg/sentry/time/calibrated_clock.go index c8cf4eca4..a98bcd7de 100644 --- a/pkg/sentry/time/calibrated_clock.go +++ b/pkg/sentry/time/calibrated_clock.go @@ -37,7 +37,7 @@ var fallbackMetric = metric.MustCreateNewUint64Metric("/time/fallback", false /* // clock. type CalibratedClock struct { // mu protects the fields below. - // TODO: consider a sequence counter for read locking. + // TODO(mpratt): consider a sequence counter for read locking. mu sync.RWMutex // ref sample the reference clock that this clock is calibrated @@ -140,7 +140,7 @@ func (c *CalibratedClock) updateParams(actual Parameters) { // N.B. logErrorAdjustment will have already logged the error // at warning level. // - // TODO: We could allow Realtime clock jumps here. + // TODO(mpratt): We could allow Realtime clock jumps here. c.resetLocked("Extreme clock error.") return } @@ -229,7 +229,7 @@ func (c *CalibratedClock) GetTime() (int64, error) { // CalibratedClocks contains calibrated monotonic and realtime clocks. // -// TODO: We know that Linux runs the monotonic and realtime clocks at +// TODO(mpratt): We know that Linux runs the monotonic and realtime clocks at // the same rate, so rather than tracking both individually, we could do one // calibration for both clocks. type CalibratedClocks struct { diff --git a/pkg/sentry/time/parameters.go b/pkg/sentry/time/parameters.go index f3ad58454..8568b1193 100644 --- a/pkg/sentry/time/parameters.go +++ b/pkg/sentry/time/parameters.go @@ -43,7 +43,7 @@ const ( // These statements assume that the host clock does not change. Actual // error will depend upon host clock changes. // - // TODO: make error correction more robust to delayed + // TODO(b/68779214): make error correction more robust to delayed // updates. ApproxUpdateInterval = 1 * time.Second diff --git a/pkg/sentry/usermem/usermem.go b/pkg/sentry/usermem/usermem.go index 99766a803..4c7d5014a 100644 --- a/pkg/sentry/usermem/usermem.go +++ b/pkg/sentry/usermem/usermem.go @@ -28,7 +28,7 @@ import ( // IO provides access to the contents of a virtual memory space. // -// FIXME: Implementations of IO cannot expect ctx to contain any +// FIXME(b/38173783): Implementations of IO cannot expect ctx to contain any // meaningful data. type IO interface { // CopyOut copies len(src) bytes from src to the memory mapped at addr. It @@ -85,7 +85,7 @@ type IO interface { // order. CopyInTo(ctx context.Context, ars AddrRangeSeq, dst safemem.Writer, opts IOOpts) (int64, error) - // TODO: The requirement that CopyOutFrom/CopyInTo call src/dst + // TODO(jamieliu): The requirement that CopyOutFrom/CopyInTo call src/dst // at most once, which is unnecessary in most cases, forces implementations // to gather safemem.Blocks into a single slice to pass to src/dst. Add // CopyOutFromIter/CopyInToIter, which relaxes this restriction, to avoid diff --git a/pkg/sentry/watchdog/watchdog.go b/pkg/sentry/watchdog/watchdog.go index c49b537a5..b4f1e3a4f 100644 --- a/pkg/sentry/watchdog/watchdog.go +++ b/pkg/sentry/watchdog/watchdog.go @@ -236,7 +236,7 @@ func (w *Watchdog) runTurn() { if !ok { // New stuck task detected. // - // TODO: Tasks blocked doing IO may be considered stuck in kernel. + // TODO(b/65849403): Tasks blocked doing IO may be considered stuck in kernel. tc = &offender{lastUpdateTime: lastUpdateTime} stuckTasks.Increment() newTaskFound = true diff --git a/pkg/syserr/syserr.go b/pkg/syserr/syserr.go index dad83e80c..232634dd4 100644 --- a/pkg/syserr/syserr.go +++ b/pkg/syserr/syserr.go @@ -49,7 +49,7 @@ func New(message string, linuxTranslation *linux.Errno) *Error { return err } - // TODO: Remove this. + // TODO(b/34162363): Remove this. errno := linuxTranslation.Number() if errno <= 0 || errno >= len(linuxBackwardsTranslations) { panic(fmt.Sprint("invalid errno: ", errno)) @@ -106,12 +106,12 @@ type linuxBackwardsTranslation struct { ok bool } -// TODO: Remove this. +// TODO(b/34162363): Remove this. var linuxBackwardsTranslations [maxErrno]linuxBackwardsTranslation // ToError translates an Error to a corresponding error value. // -// TODO: Remove this. +// TODO(b/34162363): Remove this. func (e *Error) ToError() error { if e == nil { return nil @@ -138,7 +138,7 @@ func (e *Error) ToLinux() *linux.Errno { return e.errno } -// TODO: Remove or replace most of these errors. +// TODO(b/34162363): Remove or replace most of these errors. // // Some of the errors should be replaced with package specific errors and // others should be removed entirely. @@ -278,7 +278,7 @@ var ( // FromError converts a generic error to an *Error. // -// TODO: Remove this function. +// TODO(b/34162363): Remove this function. func FromError(err error) *Error { if err == nil { return nil diff --git a/pkg/tcpip/network/ipv4/icmp.go b/pkg/tcpip/network/ipv4/icmp.go index ed9a4eee5..1c3acda4b 100644 --- a/pkg/tcpip/network/ipv4/icmp.go +++ b/pkg/tcpip/network/ipv4/icmp.go @@ -64,7 +64,7 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V } h := header.ICMPv4(v) - // TODO: Meaningfully handle all ICMP types. + // TODO(b/112892170): Meaningfully handle all ICMP types. switch h.Type() { case header.ICMPv4Echo: received.Echo.Increment() diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go index 3210e6fc7..be28be36d 100644 --- a/pkg/tcpip/network/ipv6/icmp.go +++ b/pkg/tcpip/network/ipv6/icmp.go @@ -73,7 +73,7 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V } h := header.ICMPv6(v) - // TODO: Meaningfully handle all ICMP types. + // TODO(b/112892170): Meaningfully handle all ICMP types. switch h.Type() { case header.ICMPv6PacketTooBig: received.PacketTooBig.Increment() @@ -247,7 +247,7 @@ func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack. DstAddr: r.RemoteAddress, }) - // TODO: count this in ICMP stats. + // TODO(stijlist): count this in ICMP stats. return linkEP.WritePacket(r, nil /* gso */, hdr, buffer.VectorisedView{}, ProtocolNumber) } diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index 8b6c17a90..c18571b0f 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -176,7 +176,7 @@ func (n *NIC) primaryEndpoint(protocol tcpip.NetworkProtocolNumber) *referencedN for e := list.Front(); e != nil; e = e.Next() { r := e.(*referencedNetworkEndpoint) - // TODO: allow broadcast address when SO_BROADCAST is set. + // TODO(crawshaw): allow broadcast address when SO_BROADCAST is set. switch r.ep.ID().LocalAddress { case header.IPv4Broadcast, header.IPv4Any: continue @@ -476,7 +476,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddr n.mu.RUnlock() if ok && ref.tryIncRef() { r.RemoteAddress = src - // TODO: Update the source NIC as well. + // TODO(b/123449044): Update the source NIC as well. ref.ep.HandlePacket(&r, vv) ref.decRef() } else { @@ -485,7 +485,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddr hdr := buffer.NewPrependableFromView(vv.First()) vv.RemoveFirst() - // TODO: use route.WritePacket. + // TODO(b/128629022): use route.WritePacket. if err := n.linkEP.WritePacket(&r, nil /* gso */, hdr, vv, protocol); err != nil { r.Stats().IP.OutgoingPacketErrors.Increment() } else { diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index 8f7b6f781..cb9ffe9c2 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -476,7 +476,7 @@ func (s *Stack) Stats() tcpip.Stats { // SetForwarding enables or disables the packet forwarding between NICs. func (s *Stack) SetForwarding(enable bool) { - // TODO: Expose via /proc/sys/net/ipv4/ip_forward. + // TODO(igudger, bgeffon): Expose via /proc/sys/net/ipv4/ip_forward. s.mu.Lock() s.forwarding = enable s.mu.Unlock() @@ -484,7 +484,7 @@ func (s *Stack) SetForwarding(enable bool) { // Forwarding returns if the packet forwarding between NICs is enabled. func (s *Stack) Forwarding() bool { - // TODO: Expose via /proc/sys/net/ipv4/ip_forward. + // TODO(igudger, bgeffon): Expose via /proc/sys/net/ipv4/ip_forward. s.mu.RLock() defer s.mu.RUnlock() return s.forwarding diff --git a/pkg/tcpip/stack/stack_global_state.go b/pkg/tcpip/stack/stack_global_state.go index f2c6c9a8d..3d7e4b719 100644 --- a/pkg/tcpip/stack/stack_global_state.go +++ b/pkg/tcpip/stack/stack_global_state.go @@ -15,5 +15,5 @@ package stack // StackFromEnv is the global stack created in restore run. -// FIXME +// FIXME(b/36201077) var StackFromEnv *Stack diff --git a/pkg/tcpip/stack/transport_test.go b/pkg/tcpip/stack/transport_test.go index 0c2589083..2df974bf2 100644 --- a/pkg/tcpip/stack/transport_test.go +++ b/pkg/tcpip/stack/transport_test.go @@ -453,7 +453,7 @@ func TestTransportForwarding(t *testing.T) { s := stack.New([]string{"fakeNet"}, []string{"fakeTrans"}, stack.Options{}) s.SetForwarding(true) - // TODO: Change this to a channel NIC. + // TODO(b/123449044): Change this to a channel NIC. id1 := loopback.New() if err := s.CreateNIC(1, id1); err != nil { t.Fatalf("CreateNIC #1 failed: %v", err) diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index 80cd6b4e5..b09137f08 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -444,7 +444,7 @@ type PasscredOption int // TCPInfoOption is used by GetSockOpt to expose TCP statistics. // -// TODO: Add and populate stat fields. +// TODO(b/64800844): Add and populate stat fields. type TCPInfoOption struct { RTT time.Duration RTTVar time.Duration diff --git a/pkg/tcpip/transport/raw/raw.go b/pkg/tcpip/transport/raw/raw.go index 8dada2e4f..f0f60ce91 100644 --- a/pkg/tcpip/transport/raw/raw.go +++ b/pkg/tcpip/transport/raw/raw.go @@ -100,7 +100,7 @@ type endpoint struct { } // NewEndpoint returns a raw endpoint for the given protocols. -// TODO: IP_HDRINCL, IPPROTO_RAW, and AF_PACKET. +// TODO(b/129292371): IP_HDRINCL, IPPROTO_RAW, and AF_PACKET. func NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) { if netProto != header.IPv4ProtocolNumber { return nil, tcpip.ErrUnknownProtocol diff --git a/pkg/tcpip/transport/tcp/BUILD b/pkg/tcpip/transport/tcp/BUILD index e5c05f8c0..d44d63e95 100644 --- a/pkg/tcpip/transport/tcp/BUILD +++ b/pkg/tcpip/transport/tcp/BUILD @@ -73,7 +73,7 @@ go_test( "tcp_test.go", "tcp_timestamp_test.go", ], - # FIXME + # FIXME(b/68809571) tags = ["flaky"], deps = [ ":tcp", diff --git a/pkg/unet/unet.go b/pkg/unet/unet.go index deeea078d..114fb8c5b 100644 --- a/pkg/unet/unet.go +++ b/pkg/unet/unet.go @@ -211,7 +211,7 @@ func SocketPair(packet bool) (*Socket, *Socket, error) { // variable between our two sockets. We only use SocketPair in tests // anyway. // - // NOTE: This is purely due to the fact that the raw + // NOTE(b/27107811): This is purely due to the fact that the raw // syscall does not serve as a boundary for the sanitizer. var race int32 a, err := NewSocket(fds[0]) diff --git a/pkg/unet/unet_test.go b/pkg/unet/unet_test.go index ecc670925..db5485539 100644 --- a/pkg/unet/unet_test.go +++ b/pkg/unet/unet_test.go @@ -40,7 +40,7 @@ func randomFilename() (string, error) { return "", err } - // NOTE: We try to use relative path if possible. This is + // NOTE(b/26918832): We try to use relative path if possible. This is // to help conforming to the unix path length limit. if rel, err := filepath.Rel(cwd, file); err == nil { return rel, nil diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 2488981f9..712c50ee9 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -231,7 +231,7 @@ func (cm *containerManager) Start(args *StartArgs, _ *struct{}) error { } // Prevent CIDs containing ".." from confusing the sentry when creating // /containers/ directory. - // TODO: Once we have multiple independent roots, this + // TODO(b/129293409): Once we have multiple independent roots, this // check won't be necessary. if path.Clean(args.CID) != args.CID { return fmt.Errorf("container ID shouldn't contain directory traversals such as \"..\": %q", args.CID) @@ -352,7 +352,7 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error { return fmt.Errorf("creating network: %v", err) } if eps, ok := networkStack.(*epsocket.Stack); ok { - stack.StackFromEnv = eps.Stack // FIXME + stack.StackFromEnv = eps.Stack // FIXME(b/36201077) } info, err := o.FilePayload.Files[0].Stat() if err != nil { diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 761142d98..07061b9b3 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -274,7 +274,7 @@ func getMountNameAndOptions(conf *Config, m specs.Mount, fds *fdDispenser) (stri useOverlay = conf.Overlay && !mountFlags(m.Options).ReadOnly default: - // TODO: Support all the mount types and make this a + // TODO(nlacasse): Support all the mount types and make this a // fatal error. Most applications will "just work" without // them, so this is a warning for now. // we do not support. @@ -425,7 +425,7 @@ func addRestoreMount(conf *Config, renv *fs.RestoreEnvironment, m specs.Mount, f if err != nil { return err } - // TODO: Fix this when we support all the mount types and + // TODO(nlacasse): Fix this when we support all the mount types and // make this a fatal error. if fsName == "" { return nil @@ -475,7 +475,7 @@ func createRestoreEnvironment(spec *specs.Spec, conf *Config, fds *fdDispenser) } } - // TODO: handle '/tmp' properly (see mountTmp()). + // TODO(b/67958150): handle '/tmp' properly (see mountTmp()). if !tmpMounted { tmpMount := specs.Mount{ Type: tmpfs, diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 48ecb2626..75ec19c32 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -577,7 +577,7 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config // sentry currently supports only 1 mount namespace, which is tied to a // single user namespace. Thus we must run in the same user namespace // to access mounts. - // TODO: Create a new mount namespace for the container. + // TODO(b/63601033): Create a new mount namespace for the container. creds := auth.NewUserCredentials( auth.KUID(spec.Process.User.UID), auth.KGID(spec.Process.User.GID), diff --git a/runsc/cmd/checkpoint.go b/runsc/cmd/checkpoint.go index d8f748aa0..f722df055 100644 --- a/runsc/cmd/checkpoint.go +++ b/runsc/cmd/checkpoint.go @@ -105,7 +105,7 @@ func (c *Checkpoint) Execute(_ context.Context, f *flag.FlagSet, args ...interfa return subcommands.ExitSuccess } - // TODO: Make it possible to restore into same container. + // TODO(b/110843694): Make it possible to restore into same container. // For now, we can fake it by destroying the container and making a // new container with the same ID. This hack does not work with docker // which uses the container pid to ensure that the restore-container is diff --git a/runsc/container/container.go b/runsc/container/container.go index 1bed1a97e..a30c217f7 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -529,7 +529,7 @@ func (c *Container) WaitPID(pid int32, clearStatus bool) (syscall.WaitStatus, er // SignalContainer sends the signal to the container. If all is true and signal // is SIGKILL, then waits for all processes to exit before returning. // SignalContainer returns an error if the container is already stopped. -// TODO: Distinguish different error types. +// TODO(b/113680494): Distinguish different error types. func (c *Container) SignalContainer(sig syscall.Signal, all bool) error { log.Debugf("Signal container %q: %v", c.ID, sig) // Signaling container in Stopped state is allowed. When all=false, diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 9fe584aa3..603c4d929 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -242,10 +242,10 @@ func configs(opts ...configOption) []*boot.Config { case overlay: c.Overlay = true case kvm: - // TODO: KVM tests are flaky. Disable until fixed. + // TODO(b/112165693): KVM tests are flaky. Disable until fixed. continue - // TODO: KVM doesn't work with --race. + // TODO(b/68787993): KVM doesn't work with --race. if testutil.RaceEnabled { continue } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 92495c69e..48a0dafe2 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -267,7 +267,7 @@ func (s *Sandbox) Event(cid string) (*boot.Event, error) { defer conn.Close() var e boot.Event - // TODO: Pass in the container id (cid) here. The sandbox + // TODO(b/129292330): Pass in the container id (cid) here. The sandbox // should return events only for that container. if err := conn.Call(boot.ContainerEvent, nil, &e); err != nil { return nil, fmt.Errorf("retrieving event data from sandbox: %v", err) @@ -457,7 +457,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } if conf.Platform == boot.PlatformPtrace { - // TODO: Also set a new PID namespace so that we limit + // TODO(b/75837838): Also set a new PID namespace so that we limit // access to other host processes. log.Infof("Sandbox will be started in the current PID namespace") } else { @@ -520,7 +520,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // root for itself, so it has to have the CAP_SYS_ADMIN // capability. // - // FIXME: The current implementations of + // FIXME(b/122554829): The current implementations of // os/exec doesn't allow to set ambient capabilities if // a process is started in a new user namespace. As a // workaround, we start the sandbox process with the 0 diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 32f81b8d4..ac85bec71 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -90,7 +90,7 @@ func ValidateSpec(spec *specs.Spec) error { log.Warningf("AppArmor profile %q is being ignored", spec.Process.ApparmorProfile) } - // TODO: Apply seccomp to application inside sandbox. + // TODO(b/72226747): Apply seccomp to application inside sandbox. if spec.Linux != nil && spec.Linux.Seccomp != nil { log.Warningf("Seccomp spec is being ignored") } @@ -220,7 +220,7 @@ func Capabilities(enableRaw bool, specCaps *specs.LinuxCapabilities) (*auth.Task if caps.PermittedCaps, err = capsFromNames(specCaps.Permitted, skipSet); err != nil { return nil, err } - // TODO: Support ambient capabilities. + // TODO(nlacasse): Support ambient capabilities. } return &caps, nil } diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 94e0f24e0..d35f59433 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -277,7 +277,7 @@ syscall_test(test = "//test/syscalls/linux:sendfile_test") syscall_test(test = "//test/syscalls/linux:sigaction_test") -# TODO: Enable once the test passes in runsc. +# TODO(b/119826902): Enable once the test passes in runsc. # syscall_test(test = "//test/syscalls/linux:sigaltstack_test") syscall_test(test = "//test/syscalls/linux:sigiret_test") @@ -414,7 +414,7 @@ syscall_test( ) syscall_test( - # NOTE: Large sendmsg may stall a long time. + # NOTE(b/116636318): Large sendmsg may stall a long time. size = "enormous", test = "//test/syscalls/linux:socket_unix_dgram_local_test", ) @@ -437,7 +437,7 @@ syscall_test( ) syscall_test( - # NOTE: Large sendmsg may stall a long time. + # NOTE(b/116636318): Large sendmsg may stall a long time. size = "enormous", test = "//test/syscalls/linux:socket_unix_seqpacket_local_test", ) diff --git a/test/syscalls/build_defs.bzl b/test/syscalls/build_defs.bzl index 610b030b2..cd74a769d 100644 --- a/test/syscalls/build_defs.bzl +++ b/test/syscalls/build_defs.bzl @@ -78,10 +78,10 @@ def _syscall_test( tags += [full_platform, "file_" + file_access] # Add tag to prevent the tests from running in a Bazel sandbox. - # TODO: Make the tests run without this tag. + # TODO(b/120560048): Make the tests run without this tag. tags.append("no-sandbox") - # TODO: KVM tests are tagged "manual" to until the platform is + # TODO(b/112165693): KVM tests are tagged "manual" to until the platform is # more stable. if platform == "kvm": tags += ["manual"] diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc index 230648c9b..78baf548e 100644 --- a/test/syscalls/linux/32bit.cc +++ b/test/syscalls/linux/32bit.cc @@ -80,11 +80,11 @@ constexpr int kExitCode = 42; TEST(Syscall32Bit, Int80) { switch (GvisorPlatform()) { case Platform::kKVM: - // TODO: 32-bit segments are broken (but not explictly + // TODO(b/111805002): 32-bit segments are broken (but not explictly // disabled). return; case Platform::kPtrace: - // TODO: The ptrace platform does not have a + // TODO(gvisor.dev/issue/167): The ptrace platform does not have a // consistent story here. return; case Platform::kNative: @@ -99,10 +99,10 @@ TEST(Syscall32Bit, Int80) { TEST(Syscall32Bit, Sysenter) { switch (GvisorPlatform()) { case Platform::kKVM: - // TODO: See above. + // TODO(b/111805002): See above. return; case Platform::kPtrace: - // TODO: See above. + // TODO(gvisor.dev/issue/167): See above. return; case Platform::kNative: break; @@ -123,10 +123,10 @@ TEST(Syscall32Bit, Sysenter) { TEST(Syscall32Bit, Syscall) { switch (GvisorPlatform()) { case Platform::kKVM: - // TODO: See above. + // TODO(b/111805002): See above. return; case Platform::kPtrace: - // TODO: See above. + // TODO(gvisor.dev/issue/167): See above. return; case Platform::kNative: break; @@ -207,7 +207,7 @@ void FarCall32() { TEST(Call32Bit, Disallowed) { switch (GvisorPlatform()) { case Platform::kKVM: - // TODO: See above. + // TODO(b/111805002): See above. return; case Platform::kPtrace: // The ptrace platform cannot prevent switching to compatibility mode. diff --git a/test/syscalls/linux/aio.cc b/test/syscalls/linux/aio.cc index 06643ccb8..b96aab9b9 100644 --- a/test/syscalls/linux/aio.cc +++ b/test/syscalls/linux/aio.cc @@ -103,7 +103,7 @@ TEST_F(AIOTest, BasicWrite) { // aio implementation uses aio_ring. gVisor doesn't and returns all zeroes. // Linux implements aio_ring, so skip the zeroes check. // - // TODO: Remove when gVisor implements aio_ring. + // TODO(b/65486370): Remove when gVisor implements aio_ring. auto ring = reinterpret_cast(ctx_); auto magic = IsRunningOnGvisor() ? 0 : AIO_RING_MAGIC; EXPECT_EQ(ring->magic, magic); diff --git a/test/syscalls/linux/chmod.cc b/test/syscalls/linux/chmod.cc index 2f2ff3b7d..2f42fe326 100644 --- a/test/syscalls/linux/chmod.cc +++ b/test/syscalls/linux/chmod.cc @@ -235,7 +235,7 @@ TEST(ChmodTest, FchmodFileToNoPermissionsSucceeds_NoRandomSave) { // Verify that we can get a RW FD after chmod, even if a RO fd is left open. TEST(ChmodTest, ChmodWritableWithOpenFD) { - // FIXME: broken on hostfs. + // FIXME(b/72455313): broken on hostfs. if (IsRunningOnGvisor()) { return; } diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc index 7b1d83ad8..b4a3bfcba 100644 --- a/test/syscalls/linux/epoll.cc +++ b/test/syscalls/linux/epoll.cc @@ -56,7 +56,7 @@ TEST(EpollTest, AllWritable) { struct epoll_event result[kFDsPerEpoll]; ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), SyscallSucceedsWithValue(kFDsPerEpoll)); - // TODO: Why do some tests check epoll_event::data, and others + // TODO(edahlgren): Why do some tests check epoll_event::data, and others // don't? Does Linux actually guarantee that, in any of these test cases, // epoll_wait will necessarily write out the epoll_events in the order that // they were registered? diff --git a/test/syscalls/linux/exec_binary.cc b/test/syscalls/linux/exec_binary.cc index 187696ed9..c10d85398 100644 --- a/test/syscalls/linux/exec_binary.cc +++ b/test/syscalls/linux/exec_binary.cc @@ -285,7 +285,7 @@ ElfBinary<64> StandardElf() { elf.header.e_phoff = sizeof(elf.header); elf.header.e_phentsize = sizeof(decltype(elf)::ElfPhdr); - // TODO: Always include a PT_GNU_STACK segment to + // TODO(gvisor.dev/issue/153): Always include a PT_GNU_STACK segment to // disable executable stacks. With this omitted the stack (and all PROT_READ) // mappings should be executable, but gVisor doesn't support that. decltype(elf)::ElfPhdr phdr = {}; @@ -403,7 +403,7 @@ TEST(ElfTest, DataSegment) { // Linux will allow PT_LOAD segments to overlap. TEST(ElfTest, DirectlyOverlappingSegments) { - // NOTE: see PIEOutOfOrderSegments. + // NOTE(b/37289926): see PIEOutOfOrderSegments. SKIP_IF(IsRunningOnGvisor()); ElfBinary<64> elf = StandardElf(); @@ -439,7 +439,7 @@ TEST(ElfTest, DirectlyOverlappingSegments) { // Linux allows out-of-order PT_LOAD segments. TEST(ElfTest, OutOfOrderSegments) { - // NOTE: see PIEOutOfOrderSegments. + // NOTE(b/37289926): see PIEOutOfOrderSegments. SKIP_IF(IsRunningOnGvisor()); ElfBinary<64> elf = StandardElf(); @@ -670,7 +670,7 @@ TEST(ElfTest, PIENonZeroStart) { } TEST(ElfTest, PIEOutOfOrderSegments) { - // TODO: This triggers a bug in Linux where it computes the size + // TODO(b/37289926): This triggers a bug in Linux where it computes the size // of the binary as 0x20000 - 0x40000 = 0xfffffffffffe0000, which obviously // fails to map. // @@ -1005,7 +1005,7 @@ TEST(ElfTest, NoExecute) { // Execute, but no read permissions on the binary works just fine. TEST(ElfTest, NoRead) { - // TODO: gVisor's backing filesystem may prevent the + // TODO(gvisor.dev/issue/160): gVisor's backing filesystem may prevent the // sentry from reading the executable. SKIP_IF(IsRunningOnGvisor()); @@ -1024,7 +1024,7 @@ TEST(ElfTest, NoRead) { ASSERT_NO_ERRNO(WaitStopped(child)); - // TODO: A task with a non-readable executable is marked + // TODO(gvisor.dev/issue/160): A task with a non-readable executable is marked // non-dumpable, preventing access to proc files. gVisor does not implement // this behavior. } diff --git a/test/syscalls/linux/file_base.h b/test/syscalls/linux/file_base.h index 19c9a5053..43f568111 100644 --- a/test/syscalls/linux/file_base.h +++ b/test/syscalls/linux/file_base.h @@ -52,7 +52,7 @@ class FileTest : public ::testing::Test { test_file_fd_ = ASSERT_NO_ERRNO_AND_VALUE( Open(test_file_name_, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)); - // FIXME: enable when mknod syscall is supported. + // FIXME(edahlgren): enable when mknod syscall is supported. // test_fifo_name_ = NewTempAbsPath(); // ASSERT_THAT(mknod(test_fifo_name_.c_str()), S_IFIFO|0644, 0, // SyscallSucceeds()); @@ -97,7 +97,7 @@ class FileTest : public ::testing::Test { UnlinkFile(); ClosePipes(); - // FIXME: enable when mknod syscall is supported. + // FIXME(edahlgren): enable when mknod syscall is supported. // close(test_fifo_[0]); // close(test_fifo_[1]); // unlink(test_fifo_name_.c_str()); diff --git a/test/syscalls/linux/ioctl.cc b/test/syscalls/linux/ioctl.cc index de29047e0..c7741a177 100644 --- a/test/syscalls/linux/ioctl.cc +++ b/test/syscalls/linux/ioctl.cc @@ -158,7 +158,7 @@ TEST_F(IoctlTest, FIOASYNCNoTarget) { } TEST_F(IoctlTest, FIOASYNCSelfTarget) { - // FIXME: gVisor erroneously sends SIGIO on close(2), which would + // FIXME(b/120624367): gVisor erroneously sends SIGIO on close(2), which would // kill the test when pair goes out of scope. Temporarily ignore SIGIO so that // that the close signal is ignored. struct sigaction sa; @@ -195,7 +195,7 @@ TEST_F(IoctlTest, FIOASYNCSelfTarget) { // Equivalent to FIOASYNCSelfTarget except that FIOSETOWN is called before // FIOASYNC. TEST_F(IoctlTest, FIOASYNCSelfTarget2) { - // FIXME: gVisor erroneously sends SIGIO on close(2), which would + // FIXME(b/120624367): gVisor erroneously sends SIGIO on close(2), which would // kill the test when pair goes out of scope. Temporarily ignore SIGIO so that // that the close signal is ignored. struct sigaction sa; diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc index 4ad787cc0..0a149c2e5 100644 --- a/test/syscalls/linux/ip_socket_test_util.cc +++ b/test/syscalls/linux/ip_socket_test_util.cc @@ -24,7 +24,7 @@ namespace gvisor { namespace testing { PosixErrorOr InterfaceIndex(std::string name) { - // TODO: Consider using netlink. + // TODO(igudger): Consider using netlink. ifreq req = {}; memcpy(req.ifr_name, name.c_str(), name.size()); ASSIGN_OR_RETURN_ERRNO(auto sock, Socket(AF_INET, SOCK_DGRAM, 0)); diff --git a/test/syscalls/linux/lseek.cc b/test/syscalls/linux/lseek.cc index fb6a1546e..6a4f1423c 100644 --- a/test/syscalls/linux/lseek.cc +++ b/test/syscalls/linux/lseek.cc @@ -194,7 +194,7 @@ TEST(LseekTest, EtcPasswdDup) { ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); } -// TODO: Add tests where we have donated in sockets. +// TODO(magi): Add tests where we have donated in sockets. } // namespace diff --git a/test/syscalls/linux/mkdir.cc b/test/syscalls/linux/mkdir.cc index 84db45eb3..50807b68f 100644 --- a/test/syscalls/linux/mkdir.cc +++ b/test/syscalls/linux/mkdir.cc @@ -36,7 +36,7 @@ class MkdirTest : public ::testing::Test { // TearDown unlinks created files. void TearDown() override { - // FIXME: We don't currently implement rmdir. + // FIXME(edahlgren): We don't currently implement rmdir. // We do this unconditionally because there's no harm in trying. rmdir(dirname_.c_str()); } diff --git a/test/syscalls/linux/mmap.cc b/test/syscalls/linux/mmap.cc index b500e79a4..a4fb9d1e0 100644 --- a/test/syscalls/linux/mmap.cc +++ b/test/syscalls/linux/mmap.cc @@ -816,7 +816,7 @@ class MMapFileTest : public MMapTest { // MAP_POPULATE allowed. // There isn't a good way to verify it actually did anything. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, MapPopulate) { ASSERT_THAT( Map(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd_.get(), 0), @@ -825,7 +825,7 @@ TEST_F(MMapFileTest, MapPopulate) { // MAP_POPULATE on a short file. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, MapPopulateShort) { ASSERT_THAT(Map(0, 2 * kPageSize, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd_.get(), 0), @@ -923,7 +923,7 @@ TEST_F(MMapFileTest, WriteSharedOnReadOnlyFd) { // MAP_SHARED PROT_READ not allowed on write-only FDs. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, ReadSharedOnWriteOnlyFd) { const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY)); @@ -936,7 +936,7 @@ TEST_F(MMapFileTest, ReadSharedOnWriteOnlyFd) { // MAP_SHARED PROT_WRITE not allowed on write-only FDs. // The FD must always be readable. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, WriteSharedOnWriteOnlyFd) { const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY)); @@ -1371,7 +1371,7 @@ TEST_F(MMapFileTest, WritePrivate) { // SIGBUS raised when writing past end of file to a private mapping. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, SigBusDeathWritePrivate) { SetupGvisorDeathTest(); @@ -1390,7 +1390,7 @@ TEST_F(MMapFileTest, SigBusDeathWritePrivate) { // SIGBUS raised when reading past end of file on a shared mapping. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, SigBusDeathReadShared) { SetupGvisorDeathTest(); @@ -1410,7 +1410,7 @@ TEST_F(MMapFileTest, SigBusDeathReadShared) { // SIGBUS raised when reading past end of file on a shared mapping. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, SigBusDeathWriteShared) { SetupGvisorDeathTest(); @@ -1459,7 +1459,7 @@ TEST_F(MMapFileTest, NoSigBusOnPageContainingEOFWritePrivate) { // Tests that SIGBUS is not raised when reading from a file-mapped page // containing EOF, *after* the EOF for a shared mapping. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, NoSigBusOnPageContainingEOFReadShared) { uintptr_t addr; ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), @@ -1476,7 +1476,7 @@ TEST_F(MMapFileTest, NoSigBusOnPageContainingEOFReadShared) { // Tests that SIGBUS is not raised when writing to a file-mapped page containing // EOF, *after* the EOF for a shared mapping. // -// FIXME: Parameterize. +// FIXME(b/37222275): Parameterize. TEST_F(MMapFileTest, NoSigBusOnPageContainingEOFWriteShared) { uintptr_t addr; ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc index cdc226300..22e4666c2 100644 --- a/test/syscalls/linux/open.cc +++ b/test/syscalls/linux/open.cc @@ -279,7 +279,7 @@ TEST_F(OpenTest, Null) { ASSERT_THAT(open(&c, O_RDONLY), SyscallFailsWithErrno(ENOENT)); } -// NOTE: While the man pages specify that this behavior should be +// NOTE(b/119785738): While the man pages specify that this behavior should be // undefined, Linux truncates the file on opening read only if we have write // permission, so we will too. TEST_F(OpenTest, CanTruncateReadOnly) { diff --git a/test/syscalls/linux/partial_bad_buffer.cc b/test/syscalls/linux/partial_bad_buffer.cc index 073a6b8c1..71288ebc4 100644 --- a/test/syscalls/linux/partial_bad_buffer.cc +++ b/test/syscalls/linux/partial_bad_buffer.cc @@ -158,7 +158,7 @@ TEST_F(PartialBadBufferTest, PreadvSmall) { } TEST_F(PartialBadBufferTest, WriteBig) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -168,7 +168,7 @@ TEST_F(PartialBadBufferTest, WriteBig) { } TEST_F(PartialBadBufferTest, WriteSmall) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -178,7 +178,7 @@ TEST_F(PartialBadBufferTest, WriteSmall) { } TEST_F(PartialBadBufferTest, PwriteBig) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -188,7 +188,7 @@ TEST_F(PartialBadBufferTest, PwriteBig) { } TEST_F(PartialBadBufferTest, PwriteSmall) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -198,7 +198,7 @@ TEST_F(PartialBadBufferTest, PwriteSmall) { } TEST_F(PartialBadBufferTest, WritevBig) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -211,7 +211,7 @@ TEST_F(PartialBadBufferTest, WritevBig) { } TEST_F(PartialBadBufferTest, WritevSmall) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -224,7 +224,7 @@ TEST_F(PartialBadBufferTest, WritevSmall) { } TEST_F(PartialBadBufferTest, PwritevBig) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -238,7 +238,7 @@ TEST_F(PartialBadBufferTest, PwritevBig) { } TEST_F(PartialBadBufferTest, PwritevSmall) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); @@ -279,7 +279,7 @@ TEST_F(PartialBadBufferTest, GetdentsOneEntry) { // Verify that when write returns EFAULT the kernel hasn't silently written // the initial valid bytes. TEST_F(PartialBadBufferTest, WriteEfaultIsntPartial) { - // FIXME: The sentry write syscalls will return immediately + // FIXME(b/24788078): The sentry write syscalls will return immediately // if Access returns an error, but Access may not return an error // and the sentry will instead perform a partial write. SKIP_IF(IsRunningOnGvisor()); diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc index c49ec9f09..abd10b11b 100644 --- a/test/syscalls/linux/pipe.cc +++ b/test/syscalls/linux/pipe.cc @@ -36,7 +36,7 @@ namespace { // Buffer size of a pipe. // -// TODO: Get this from F_GETPIPE_SZ. +// TODO(b/35762278): Get this from F_GETPIPE_SZ. constexpr int kPipeSize = 65536; class PipeTest : public ::testing::Test { @@ -316,7 +316,7 @@ TEST_F(PipeTest, BlockWriteClosed) { // Blocking write returns EPIPE when read end is closed even if something has // been written. // -// FIXME: Pipe writes blocking early allows S/R to interrupt the +// FIXME(b/35924046): Pipe writes blocking early allows S/R to interrupt the // write(2) call before the buffer is full. Then the next call will will return // non-zero instead of EPIPE. TEST_F(PipeTest, BlockPartialWriteClosed_NoRandomSave) { @@ -329,7 +329,7 @@ TEST_F(PipeTest, BlockPartialWriteClosed_NoRandomSave) { // Write more than fits in the buffer. Blocks then returns partial write // when the other end is closed. The next call returns EPIPE. if (IsRunningOnGvisor()) { - // FIXME: Pipe writes block early on gVisor, resulting in a + // FIXME(b/35924046): Pipe writes block early on gVisor, resulting in a // shorter than expected partial write. ASSERT_THAT(write(wfd, buf.data(), buf.size()), SyscallSucceedsWithValue(::testing::Gt(0))); diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc index 3ec31ae8b..7ba274226 100644 --- a/test/syscalls/linux/proc.cc +++ b/test/syscalls/linux/proc.cc @@ -61,7 +61,7 @@ #include "test/util/thread_util.h" #include "test/util/timer_util.h" -// NOTE: No, this isn't really a syscall but this is a really simple +// NOTE(magi): No, this isn't really a syscall but this is a really simple // way to get it tested on both gVisor, PTrace and Linux. using ::testing::AllOf; @@ -489,7 +489,7 @@ TEST(ProcSelfMaps, Map1) { } TEST(ProcSelfMaps, Map2) { - // NOTE: The permissions must be different or the pages will get merged. + // NOTE(magi): The permissions must be different or the pages will get merged. Mapping map1 = ASSERT_NO_ERRNO_AND_VALUE( MmapAnon(kPageSize, PROT_READ | PROT_EXEC, MAP_PRIVATE)); Mapping map2 = @@ -564,7 +564,7 @@ TEST(ProcSelfMaps, MapUnmap) { } TEST(ProcSelfMaps, Mprotect) { - // FIXME: Linux's mprotect() sometimes fails to merge VMAs in this + // FIXME(jamieliu): Linux's mprotect() sometimes fails to merge VMAs in this // case. SKIP_IF(!IsRunningOnGvisor()); @@ -977,7 +977,7 @@ void MapPopulateRSS(int prot, uint64_t* before, uint64_t* after) { *after = ASSERT_NO_ERRNO_AND_VALUE(CurrentRSS()); } -// TODO: Test for PROT_READ + MAP_POPULATE anonymous mappings. Their +// TODO(b/73896574): Test for PROT_READ + MAP_POPULATE anonymous mappings. Their // semantics are more subtle: // // Small pages -> Zero page mapped, not counted in RSS @@ -1140,7 +1140,7 @@ TEST(ProcPidStatusTest, ValuesAreTabDelimited) { // Threads properly counts running threads. // -// TODO: Test zombied threads while the thread group leader is still +// TODO(mpratt): Test zombied threads while the thread group leader is still // running with generalized fork and clone children from the wait test. TEST(ProcPidStatusTest, Threads) { char buf[4096] = {}; @@ -1274,7 +1274,7 @@ TEST(ProcPidSymlink, SubprocessRunning) { SyscallSucceedsWithValue(sizeof(buf))); } -// FIXME: Inconsistent behavior between gVisor and linux +// FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux // on proc files. TEST(ProcPidSymlink, SubprocessZombied) { ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); @@ -1298,13 +1298,13 @@ TEST(ProcPidSymlink, SubprocessZombied) { SyscallFailsWithErrno(want)); } - // FIXME: Inconsistent behavior between gVisor and linux + // FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux // on proc files. // 4.17 & gVisor: Syscall succeeds and returns 1 // EXPECT_THAT(ReadlinkWhileZombied("ns/pid", buf, sizeof(buf)), // SyscallFailsWithErrno(EACCES)); - // FIXME: Inconsistent behavior between gVisor and linux + // FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux // on proc files. // 4.17 & gVisor: Syscall succeeds and returns 1. // EXPECT_THAT(ReadlinkWhileZombied("ns/user", buf, sizeof(buf)), @@ -1313,7 +1313,7 @@ TEST(ProcPidSymlink, SubprocessZombied) { // Test whether /proc/PID/ symlinks can be read for an exited process. TEST(ProcPidSymlink, SubprocessExited) { - // FIXME: These all succeed on gVisor. + // FIXME(gvisor.dev/issue/164): These all succeed on gVisor. SKIP_IF(IsRunningOnGvisor()); char buf[1]; @@ -1404,7 +1404,7 @@ TEST(ProcPidFile, SubprocessZombie) { EXPECT_THAT(ReadWhileZombied("uid_map", buf, sizeof(buf)), SyscallSucceedsWithValue(sizeof(buf))); - // FIXME: Inconsistent behavior between gVisor and linux + // FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux // on proc files. // gVisor & 4.17: Succeeds and returns 1. // EXPECT_THAT(ReadWhileZombied("io", buf, sizeof(buf)), @@ -1415,7 +1415,7 @@ TEST(ProcPidFile, SubprocessZombie) { TEST(ProcPidFile, SubprocessExited) { char buf[1]; - // FIXME: Inconsistent behavior between kernels + // FIXME(gvisor.dev/issue/164): Inconsistent behavior between kernels // gVisor: Fails with ESRCH. // 4.17: Succeeds and returns 1. // EXPECT_THAT(ReadWhileExited("auxv", buf, sizeof(buf)), @@ -1425,7 +1425,7 @@ TEST(ProcPidFile, SubprocessExited) { SyscallFailsWithErrno(ESRCH)); if (!IsRunningOnGvisor()) { - // FIXME: Succeeds on gVisor. + // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. EXPECT_THAT(ReadWhileExited("comm", buf, sizeof(buf)), SyscallFailsWithErrno(ESRCH)); } @@ -1434,25 +1434,25 @@ TEST(ProcPidFile, SubprocessExited) { SyscallSucceedsWithValue(sizeof(buf))); if (!IsRunningOnGvisor()) { - // FIXME: Succeeds on gVisor. + // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. EXPECT_THAT(ReadWhileExited("io", buf, sizeof(buf)), SyscallFailsWithErrno(ESRCH)); } if (!IsRunningOnGvisor()) { - // FIXME: Returns EOF on gVisor. + // FIXME(gvisor.dev/issue/164): Returns EOF on gVisor. EXPECT_THAT(ReadWhileExited("maps", buf, sizeof(buf)), SyscallFailsWithErrno(ESRCH)); } if (!IsRunningOnGvisor()) { - // FIXME: Succeeds on gVisor. + // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. EXPECT_THAT(ReadWhileExited("stat", buf, sizeof(buf)), SyscallFailsWithErrno(ESRCH)); } if (!IsRunningOnGvisor()) { - // FIXME: Succeeds on gVisor. + // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. EXPECT_THAT(ReadWhileExited("status", buf, sizeof(buf)), SyscallFailsWithErrno(ESRCH)); } diff --git a/test/syscalls/linux/proc_pid_smaps.cc b/test/syscalls/linux/proc_pid_smaps.cc index 5f9c42ce5..cf5c462f3 100644 --- a/test/syscalls/linux/proc_pid_smaps.cc +++ b/test/syscalls/linux/proc_pid_smaps.cc @@ -82,7 +82,7 @@ struct ProcPidSmapsEntry { // Given the value part of a /proc/[pid]/smaps field containing a value in kB // (for example, " 4 kB", returns the value in kB (in this example, 4). PosixErrorOr SmapsValueKb(absl::string_view value) { - // TODO: let us use RE2 or + // TODO(jamieliu): let us use RE2 or std::pair parts = absl::StrSplit(value, ' ', absl::SkipEmpty()); if (parts.second != "kB") { diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc index 1c9d7d4f4..e0c56f1fc 100644 --- a/test/syscalls/linux/ptrace.cc +++ b/test/syscalls/linux/ptrace.cc @@ -823,7 +823,7 @@ TEST(PtraceTest, TEST(PtraceTest, Int3) { switch (GvisorPlatform()) { case Platform::kKVM: - // TODO: int3 isn't handled properly. + // TODO(b/124248694): int3 isn't handled properly. return; default: break; diff --git a/test/syscalls/linux/pwrite64.cc b/test/syscalls/linux/pwrite64.cc index 60ae6de1f..485b1e48d 100644 --- a/test/syscalls/linux/pwrite64.cc +++ b/test/syscalls/linux/pwrite64.cc @@ -30,7 +30,7 @@ namespace { // This test is currently very rudimentary. // -// TODO: +// TODO(edahlgren): // * bad buffer states (EFAULT). // * bad fds (wrong permission, wrong type of file, EBADF). // * check offset is not incremented. diff --git a/test/syscalls/linux/readv_socket.cc b/test/syscalls/linux/readv_socket.cc index 2c129b7e8..cf22c395e 100644 --- a/test/syscalls/linux/readv_socket.cc +++ b/test/syscalls/linux/readv_socket.cc @@ -41,7 +41,7 @@ class ReadvSocketTest : public SocketTest { ASSERT_THAT(write(test_unix_seqpacket_socket_[1], kReadvTestData, kReadvTestDataSize), SyscallSucceedsWithValue(kReadvTestDataSize)); - // FIXME: Enable when possible. + // FIXME(b/69821513): Enable when possible. // ASSERT_THAT(write(test_tcp_socket_[1], kReadvTestData, // kReadvTestDataSize), // SyscallSucceedsWithValue(kReadvTestDataSize)); diff --git a/test/syscalls/linux/rtsignal.cc b/test/syscalls/linux/rtsignal.cc index 1f2fed7cc..ff948f9d5 100644 --- a/test/syscalls/linux/rtsignal.cc +++ b/test/syscalls/linux/rtsignal.cc @@ -75,7 +75,7 @@ class RtSignalTest : public ::testing::Test { static int rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t* uinfo) { int ret; do { - // NOTE: rt_sigqueueinfo(2) could return EAGAIN for RT signals. + // NOTE(b/25434735): rt_sigqueueinfo(2) could return EAGAIN for RT signals. ret = syscall(SYS_rt_sigqueueinfo, tgid, sig, uinfo); } while (ret == -1 && errno == EAGAIN); return ret; diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc index cdc5c0ce8..14d7827c2 100644 --- a/test/syscalls/linux/socket_inet_loopback.cc +++ b/test/syscalls/linux/socket_inet_loopback.cc @@ -221,7 +221,7 @@ TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread) { std::atomic connects_received = ATOMIC_VAR_INIT(0); std::unique_ptr listen_thread[kThreadCount]; int accept_counts[kThreadCount] = {}; - // TODO: figure how to not disable S/R for the whole test. + // TODO(avagin): figure how to not disable S/R for the whole test. // We need to take into account that this test executes a lot of system // calls from many threads. DisableSave ds; @@ -325,7 +325,7 @@ TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThread) { std::atomic packets_received = ATOMIC_VAR_INIT(0); std::unique_ptr receiver_thread[kThreadCount]; int packets_per_socket[kThreadCount] = {}; - // TODO: figure how to not disable S/R for the whole test. + // TODO(avagin): figure how to not disable S/R for the whole test. DisableSave ds; // Too expensive. for (int i = 0; i < kThreadCount; i++) { @@ -642,7 +642,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) { TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) { auto const& param = GetParam(); - // FIXME + // FIXME(b/114268588) SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM); for (int i = 0; true; i++) { @@ -743,7 +743,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) { TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) { auto const& param = GetParam(); - // FIXME + // FIXME(b/114268588) SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM); for (int i = 0; true; i++) { @@ -867,7 +867,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) { TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) { auto const& param = GetParam(); - // FIXME + // FIXME(b/114268588) SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM); for (int i = 0; true; i++) { diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc index 8b4fc57b6..9dd9e1bd6 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc @@ -244,7 +244,7 @@ TestAddress V4Multicast() { // set interface or group membership. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelfNoGroup) { - // FIXME: A group membership is not required for external + // FIXME(b/125485338): A group membership is not required for external // multicast on gVisor. SKIP_IF(IsRunningOnGvisor()); @@ -371,7 +371,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // Check that multicast packets won't be delivered to another socket with no // set interface or group membership. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) { - // FIXME: A group membership is not required for external + // FIXME(b/125485338): A group membership is not required for external // multicast on gVisor. SKIP_IF(IsRunningOnGvisor()); diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index 8d2e7d333..ed4ae1c71 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -180,7 +180,7 @@ void CheckGetLinkResponse(const struct nlmsghdr* hdr, int seq, int port) { // RTM_NEWLINK contains at least the header and ifinfomsg. EXPECT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); - // TODO: Check ifinfomsg contents and following attrs. + // TODO(mpratt): Check ifinfomsg contents and following attrs. } TEST(NetlinkRouteTest, GetLinkDump) { @@ -370,7 +370,7 @@ TEST(NetlinkRouteTest, GetAddrDump) { // RTM_NEWADDR contains at least the header and ifaddrmsg. EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg)); - // TODO: Check ifaddrmsg contents and following attrs. + // TODO(mpratt): Check ifaddrmsg contents and following attrs. })); } diff --git a/test/syscalls/linux/socket_stream_blocking.cc b/test/syscalls/linux/socket_stream_blocking.cc index 8b3f6a647..f0f86c01c 100644 --- a/test/syscalls/linux/socket_stream_blocking.cc +++ b/test/syscalls/linux/socket_stream_blocking.cc @@ -33,7 +33,7 @@ namespace gvisor { namespace testing { TEST_P(BlockingStreamSocketPairTest, BlockPartialWriteClosed) { - // FIXME: gVisor doesn't support SO_SNDBUF on UDS, nor does it + // FIXME(b/35921550): gVisor doesn't support SO_SNDBUF on UDS, nor does it // enforce any limit; it will write arbitrary amounts of data without // blocking. SKIP_IF(IsRunningOnGvisor()); diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index 035087566..0be23e541 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -353,7 +353,7 @@ PosixErrorOr> CreateTCPAcceptBindSocketPair( } MaybeSave(); // Successful accept. - // FIXME + // FIXME(b/110484944) if (connect_result == -1) { absl::SleepFor(absl::Seconds(1)); } diff --git a/test/syscalls/linux/socket_unix.cc b/test/syscalls/linux/socket_unix.cc index 7332b768e..fafb23ad1 100644 --- a/test/syscalls/linux/socket_unix.cc +++ b/test/syscalls/linux/socket_unix.cc @@ -186,7 +186,7 @@ TEST_P(UnixSocketPairTest, BasicFDPassNoSpace) { // BasicFDPassNoSpaceMsgCtrunc sends an FD, but does not provide any space to // receive it. It then verifies that the MSG_CTRUNC flag is set in the msghdr. TEST_P(UnixSocketPairTest, BasicFDPassNoSpaceMsgCtrunc) { - // FIXME: Support MSG_CTRUNC. + // FIXME(gvisor.dev/issue/206): Support MSG_CTRUNC. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -224,7 +224,7 @@ TEST_P(UnixSocketPairTest, BasicFDPassNoSpaceMsgCtrunc) { // accomidate the FD, but msg_control is set to NULL. In this case, msg_control // should override msg_controllen. TEST_P(UnixSocketPairTest, BasicFDPassNullControlMsgCtrunc) { - // FIXME: Fix handling of NULL msg_control. + // FIXME(gvisor.dev/issue/207): Fix handling of NULL msg_control. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -259,7 +259,7 @@ TEST_P(UnixSocketPairTest, BasicFDPassNullControlMsgCtrunc) { // space to receive it. It then verifies that the MSG_CTRUNC flag is set in the // msghdr. TEST_P(UnixSocketPairTest, BasicFDPassNotEnoughSpaceMsgCtrunc) { - // FIXME: Support MSG_CTRUNC. + // FIXME(gvisor.dev/issue/206): Support MSG_CTRUNC. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -296,7 +296,7 @@ TEST_P(UnixSocketPairTest, BasicFDPassNotEnoughSpaceMsgCtrunc) { // space to receive two of them. It then verifies that the MSG_CTRUNC flag is // set in the msghdr. TEST_P(UnixSocketPairTest, BasicThreeFDPassTruncationMsgCtrunc) { - // FIXME: Support MSG_CTRUNC. + // FIXME(gvisor.dev/issue/206): Support MSG_CTRUNC. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -408,7 +408,7 @@ TEST_P(UnixSocketPairTest, BasicFDPassUnalignedRecvNoMsgTrunc) { // provides enough space to receive one of them. It then verifies that the // MSG_CTRUNC flag is set in the msghdr. TEST_P(UnixSocketPairTest, BasicTwoFDPassUnalignedRecvTruncationMsgTrunc) { - // FIXME: Support MSG_CTRUNC. + // FIXME(gvisor.dev/issue/206): Support MSG_CTRUNC. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -1010,7 +1010,7 @@ TEST_P(UnixSocketPairTest, CredPassNoMsgCtrunc) { // the data without providing space for any credentials and verifies that // MSG_CTRUNC is set in the msghdr. TEST_P(UnixSocketPairTest, CredPassNoSpaceMsgCtrunc) { - // FIXME: Support MSG_CTRUNC. + // FIXME(gvisor.dev/issue/206): Support MSG_CTRUNC. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -1061,7 +1061,7 @@ TEST_P(UnixSocketPairTest, CredPassNoSpaceMsgCtrunc) { // the data while providing enough space for only the first field of the // credentials and verifies that MSG_CTRUNC is set in the msghdr. TEST_P(UnixSocketPairTest, CredPassTruncatedMsgCtrunc) { - // FIXME: Support MSG_CTRUNC. + // FIXME(gvisor.dev/issue/206): Support MSG_CTRUNC. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); @@ -1615,7 +1615,7 @@ TEST_P(UnixSocketPairTest, SocketShutdown) { } TEST_P(UnixSocketPairTest, SocketReopenFromProcfs) { - // TODO: We should be returning ENXIO and NOT EIO. + // TODO(b/122310852): We should be returning ENXIO and NOT EIO. SKIP_IF(IsRunningOnGvisor()); auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); diff --git a/test/syscalls/linux/socket_unix_dgram.cc b/test/syscalls/linux/socket_unix_dgram.cc index c17d3990f..5dd5e6d77 100644 --- a/test/syscalls/linux/socket_unix_dgram.cc +++ b/test/syscalls/linux/socket_unix_dgram.cc @@ -28,7 +28,7 @@ namespace testing { namespace { TEST_P(DgramUnixSocketPairTest, WriteOneSideClosed) { - // FIXME: gVisor datagram sockets return EPIPE instead of + // FIXME(b/35925052): gVisor datagram sockets return EPIPE instead of // ECONNREFUSED. SKIP_IF(IsRunningOnGvisor()); diff --git a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc index 460eb8320..3becb513d 100644 --- a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc +++ b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc @@ -31,7 +31,7 @@ using NonBlockingDgramUnixSocketPairTest = SocketPairTest; TEST_P(NonBlockingDgramUnixSocketPairTest, ReadOneSideClosed) { if (IsRunningOnGvisor()) { - // FIXME: gVisor datagram sockets return 0 instead of + // FIXME(b/70803293): gVisor datagram sockets return 0 instead of // EAGAIN. return; } diff --git a/test/syscalls/linux/socket_unix_non_stream.cc b/test/syscalls/linux/socket_unix_non_stream.cc index 8e0cbee4c..a565978f9 100644 --- a/test/syscalls/linux/socket_unix_non_stream.cc +++ b/test/syscalls/linux/socket_unix_non_stream.cc @@ -47,7 +47,7 @@ TEST_P(UnixNonStreamSocketPairTest, RecvMsgTooLarge) { const int ret = RetryEINTR(write)(sockets->second_fd(), write_buf.data(), write_buf.size()); if (ret < 0 && errno == ENOBUFS) { - // NOTE: Linux may stall the write for a long time and + // NOTE(b/116636318): Linux may stall the write for a long time and // ultimately return ENOBUFS. Allow this error, since a retry will likely // result in the same error. return; @@ -136,7 +136,7 @@ TEST_P(UnixNonStreamSocketPairTest, FragmentedSendMsg) { // N.B. At minimum, the socketpair gofer should provide a socket that is // already the correct size. // - // TODO: When internal UDS support SO_SNDBUF, we can assert that + // TODO(b/35921550): When internal UDS support SO_SNDBUF, we can assert that // we always get the right SO_SNDBUF on gVisor. GTEST_SKIP() << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf; } @@ -156,7 +156,7 @@ TEST_P(UnixNonStreamSocketPairTest, FragmentedSendMsg) { msg.msg_iov = &iov; msg.msg_iovlen = 1; - // NOTE: Linux has poor behavior in the presence of + // NOTE(b/116636318,b/115833655): Linux has poor behavior in the presence of // physical memory fragmentation. As a result, this may stall for a long time // and ultimately return ENOBUFS. Allow this error, since it means that we // made it to the host kernel and started the sendmsg. @@ -192,7 +192,7 @@ TEST_P(UnixNonStreamSocketPairTest, FragmentedRecvMsg) { // N.B. At minimum, the socketpair gofer should provide a socket that is // already the correct size. // - // TODO: When internal UDS support SO_SNDBUF, we can assert that + // TODO(b/35921550): When internal UDS support SO_SNDBUF, we can assert that // we always get the right SO_SNDBUF on gVisor. GTEST_SKIP() << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf; } @@ -201,7 +201,7 @@ TEST_P(UnixNonStreamSocketPairTest, FragmentedRecvMsg) { const int ret = RetryEINTR(write)(sockets->first_fd(), write_buf.data(), write_buf.size()); if (ret < 0 && errno == ENOBUFS) { - // NOTE: Linux may stall the write for a long time and + // NOTE(b/116636318): Linux may stall the write for a long time and // ultimately return ENOBUFS. Allow this error, since a retry will likely // result in the same error. return; diff --git a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc index 270d7203f..21209b244 100644 --- a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc +++ b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc @@ -42,7 +42,7 @@ TEST_P(UnboundUnixSeqpacketSocketPairTest, SendtoWithoutConnect) { } TEST_P(UnboundUnixSeqpacketSocketPairTest, SendtoWithoutConnectIgnoresAddr) { - // FIXME: gVisor tries to find /foo/bar and thus returns ENOENT. + // FIXME(b/68223466): gVisor tries to find /foo/bar and thus returns ENOENT. if (IsRunningOnGvisor()) { return; } diff --git a/test/syscalls/linux/socket_unix_unbound_stream.cc b/test/syscalls/linux/socket_unix_unbound_stream.cc index 4db5b4be1..b95f9569e 100644 --- a/test/syscalls/linux/socket_unix_unbound_stream.cc +++ b/test/syscalls/linux/socket_unix_unbound_stream.cc @@ -269,7 +269,7 @@ TEST_P(UnixStreamSocketPairTest, SinglePeek) { // 9f389e35674f5b086edd70ed524ca0f287259725 which changes this behavior. We // used to target 3.11 compatibility, so disable this test on newer kernels. // - // NOTE: Bring this up to Linux 4.4 compatibility. + // NOTE(b/118902768): Bring this up to Linux 4.4 compatibility. auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); SKIP_IF(version.major > 4 || (version.major == 4 && version.minor >= 3)); } @@ -686,7 +686,7 @@ TEST_P(UnboundUnixStreamSocketPairTest, SendtoWithoutConnect) { } TEST_P(UnboundUnixStreamSocketPairTest, SendtoWithoutConnectIgnoresAddr) { - // FIXME: gVisor tries to find /foo/bar and thus returns ENOENT. + // FIXME(b/68223466): gVisor tries to find /foo/bar and thus returns ENOENT. if (IsRunningOnGvisor()) { return; } diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc index 48a2059de..746318d09 100644 --- a/test/syscalls/linux/stat.cc +++ b/test/syscalls/linux/stat.cc @@ -416,7 +416,7 @@ TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoRandomSave) { EXPECT_EQ(st_child_before.st_gid, st_child_fd.st_gid); EXPECT_EQ(st_child_before.st_size, st_child_fd.st_size); - // TODO: This isn't ideal but since fstatfs(2) will always return + // TODO(b/34861058): This isn't ideal but since fstatfs(2) will always return // OVERLAYFS_SUPER_MAGIC we have no way to know if this fs is backed by a // gofer which doesn't support links. EXPECT_TRUE(st_child_fd.st_nlink == 0 || st_child_fd.st_nlink == 1); diff --git a/test/syscalls/linux/stat_times.cc b/test/syscalls/linux/stat_times.cc index 442957c65..8346e9a8e 100644 --- a/test/syscalls/linux/stat_times.cc +++ b/test/syscalls/linux/stat_times.cc @@ -68,7 +68,7 @@ TEST_F(StatTimesTest, FileCreationTimes) { TEST_F(StatTimesTest, FileCtimeChanges) { auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - MaybeSave(); // FIXME: ctime is inconsistent. + MaybeSave(); // FIXME(b/69865927): ctime is inconsistent. absl::Time atime, mtime, ctime; std::tie(atime, mtime, ctime) = GetTime(file); @@ -150,7 +150,7 @@ TEST_F(StatTimesTest, FileAtimeChanges) { const auto file = ASSERT_NO_ERRNO_AND_VALUE( TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), contents, 0666)); - MaybeSave(); // FIXME: ctime is inconsistent. + MaybeSave(); // FIXME(b/69865927): ctime is inconsistent. absl::Time atime, mtime, ctime; std::tie(atime, mtime, ctime) = GetTime(file); @@ -184,7 +184,7 @@ TEST_F(StatTimesTest, DirAtimeChanges) { const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - MaybeSave(); // FIXME: ctime is inconsistent. + MaybeSave(); // FIXME(b/69865927): ctime is inconsistent. absl::Time atime, mtime, ctime; std::tie(atime, mtime, ctime) = GetTime(dir); @@ -193,7 +193,7 @@ TEST_F(StatTimesTest, DirAtimeChanges) { const absl::Time before = absl::Now() - absl::Seconds(1); - // NOTE: Keep an fd open. This ensures that the inode backing the + // NOTE(b/37756234): Keep an fd open. This ensures that the inode backing the // directory won't be destroyed before the final GetTime to avoid writing out // timestamps and causing side effects. const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0)); diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 1057f5892..33620a874 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -191,7 +191,7 @@ TEST_P(TcpSocketTest, SenderAddressIgnoredOnPeek) { TEST_P(TcpSocketTest, SendtoAddressIgnored) { struct sockaddr_storage addr; memset(&addr, 0, sizeof(addr)); - addr.ss_family = GetParam(); // FIXME + addr.ss_family = GetParam(); // FIXME(b/63803955) char data = '\0'; EXPECT_THAT( diff --git a/test/syscalls/linux/tkill.cc b/test/syscalls/linux/tkill.cc index 9842ccc9b..3e8ce5327 100644 --- a/test/syscalls/linux/tkill.cc +++ b/test/syscalls/linux/tkill.cc @@ -32,7 +32,7 @@ namespace { static int tkill(pid_t tid, int sig) { int ret; do { - // NOTE: tkill(2) could return EAGAIN for RT signals. + // NOTE(b/25434735): tkill(2) could return EAGAIN for RT signals. ret = syscall(SYS_tkill, tid, sig); } while (ret == -1 && errno == EAGAIN); return ret; diff --git a/test/syscalls/linux/udp_bind.cc b/test/syscalls/linux/udp_bind.cc index 902be47d3..547eb2a6c 100644 --- a/test/syscalls/linux/udp_bind.cc +++ b/test/syscalls/linux/udp_bind.cc @@ -286,7 +286,7 @@ INSTANTIATE_TEST_SUITE_P( []() { SendtoTestParam param = {}; param.description = "connected IPv6 sendto IPv4 mapped IPv6"; - // TODO: Determine if this inconsistent behavior is worth + // TODO(igudger): Determine if this inconsistent behavior is worth // implementing. param.skip_on_gvisor = true; param.send_domain = AF_INET6; @@ -299,7 +299,7 @@ INSTANTIATE_TEST_SUITE_P( []() { SendtoTestParam param = {}; param.description = "connected IPv6 sendto IPv4"; - // TODO: Determine if this inconsistent behavior is worth + // TODO(igudger): Determine if this inconsistent behavior is worth // implementing. param.skip_on_gvisor = true; param.send_domain = AF_INET6; diff --git a/test/syscalls/linux/uidgid.cc b/test/syscalls/linux/uidgid.cc index c0c1f2960..d78a09b1e 100644 --- a/test/syscalls/linux/uidgid.cc +++ b/test/syscalls/linux/uidgid.cc @@ -169,7 +169,7 @@ TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); const gid_t gid = FLAGS_scratch_gid1; - // NOTE: Do setgid in a separate thread so that we can test if + // NOTE(b/64676707): Do setgid in a separate thread so that we can test if // info.si_pid is set correctly. ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); }); EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); diff --git a/test/syscalls/linux/utimes.cc b/test/syscalls/linux/utimes.cc index d95ee74ec..bf776cd93 100644 --- a/test/syscalls/linux/utimes.cc +++ b/test/syscalls/linux/utimes.cc @@ -33,7 +33,7 @@ namespace testing { namespace { -// TODO: utimes(nullptr) does not pick the "now" time in the +// TODO(b/36516566): utimes(nullptr) does not pick the "now" time in the // application's time domain, so when asserting that times are within a window, // we expand the window to allow for differences between the time domains. constexpr absl::Duration kClockSlack = absl::Milliseconds(100); @@ -235,7 +235,7 @@ void TestUtimensat(int dirFd, std::string const& path) { EXPECT_LE(mtime3, after); if (!IsRunningOnGvisor()) { - // FIXME: Gofers set atime and mtime to different "now" times. + // FIXME(b/36516566): Gofers set atime and mtime to different "now" times. EXPECT_EQ(atime3, mtime3); } } diff --git a/test/syscalls/linux/wait.cc b/test/syscalls/linux/wait.cc index cfab8a976..fcd606bec 100644 --- a/test/syscalls/linux/wait.cc +++ b/test/syscalls/linux/wait.cc @@ -40,7 +40,7 @@ using ::testing::UnorderedElementsAre; // These unit tests focus on the wait4(2) system call, but include a basic // checks for the i386 waitpid(2) syscall, which is a subset of wait4(2). // -// NOTE: Some functionality is not tested as +// NOTE(b/22640830,b/27680907,b/29049891): Some functionality is not tested as // it is not currently supported by gVisor: // * UID in waitid(2) siginfo. // * Process groups. diff --git a/test/syscalls/linux/write.cc b/test/syscalls/linux/write.cc index 432bd6066..7f80b2fa8 100644 --- a/test/syscalls/linux/write.cc +++ b/test/syscalls/linux/write.cc @@ -33,7 +33,7 @@ namespace testing { namespace { // This test is currently very rudimentary. // -// TODO: +// TODO(edahlgren): // * bad buffer states (EFAULT). // * bad fds (wrong permission, wrong type of file, EBADF). // * check offset is incremented. diff --git a/third_party/gvsync/downgradable_rwmutex_unsafe.go b/third_party/gvsync/downgradable_rwmutex_unsafe.go index a63a0d084..131f0a2ba 100644 --- a/third_party/gvsync/downgradable_rwmutex_unsafe.go +++ b/third_party/gvsync/downgradable_rwmutex_unsafe.go @@ -49,7 +49,7 @@ func (rw *DowngradableRWMutex) RLock() { // RUnlock undoes a single RLock call. func (rw *DowngradableRWMutex) RUnlock() { if RaceEnabled { - // TODO: Why does this need to be ReleaseMerge instead of + // TODO(jamieliu): Why does this need to be ReleaseMerge instead of // Release? IIUC this establishes Unlock happens-before RUnlock, which // seems unnecessary. RaceReleaseMerge(unsafe.Pointer(&rw.writerSem)) diff --git a/vdso/cycle_clock.h b/vdso/cycle_clock.h index 26d6690c0..309e07a3f 100644 --- a/vdso/cycle_clock.h +++ b/vdso/cycle_clock.h @@ -23,7 +23,7 @@ namespace vdso { #if __x86_64__ -// TODO: The appropriate barrier instruction to use with rdtsc on +// TODO(b/74613497): The appropriate barrier instruction to use with rdtsc on // x86_64 depends on the vendor. Intel processors can use lfence but AMD may // need mfence, depending on MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT. diff --git a/vdso/vdso_amd64.lds b/vdso/vdso_amd64.lds index 166779931..e2615ae9e 100644 --- a/vdso/vdso_amd64.lds +++ b/vdso/vdso_amd64.lds @@ -56,7 +56,7 @@ SECTIONS { .altinstr_replacement : { *(.altinstr_replacement) } /* - * TODO: Remove this alignment? Then the VDSO would fit + * TODO(gvisor.dev/issue/157): Remove this alignment? Then the VDSO would fit * in a single page. */ . = ALIGN(0x1000); diff --git a/vdso/vdso_arm64.lds b/vdso/vdso_arm64.lds index 19f8efa01..469185468 100644 --- a/vdso/vdso_arm64.lds +++ b/vdso/vdso_arm64.lds @@ -59,7 +59,7 @@ SECTIONS { .altinstr_replacement : { *(.altinstr_replacement) } /* - * TODO: Remove this alignment? Then the VDSO would fit + * TODO(gvisor.dev/issue/157): Remove this alignment? Then the VDSO would fit * in a single page. */ . = ALIGN(0x1000); -- cgit v1.2.3 From 4d52a5520101a88424fb63dd99412a1db33fbd06 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Mon, 29 Apr 2019 14:25:05 -0700 Subject: Change copyright notice to "The gVisor Authors" Based on the guidelines at https://opensource.google.com/docs/releasing/authors/. 1. $ rg -l "Google LLC" | xargs sed -i 's/Google LLC.*/The gVisor Authors./' 2. Manual fixup of "Google Inc" references. 3. Add AUTHORS file. Authors may request to be added to this file. 4. Point netstack AUTHORS to gVisor AUTHORS. Drop CONTRIBUTORS. Fixes #209 PiperOrigin-RevId: 245823212 Change-Id: I64530b24ad021a7d683137459cafc510f5ee1de9 --- AUTHORS | 8 ++++++++ kokoro/run_build.sh | 2 +- kokoro/run_tests.sh | 2 +- pkg/abi/abi.go | 2 +- pkg/abi/abi_linux.go | 2 +- pkg/abi/flag.go | 2 +- pkg/abi/linux/aio.go | 2 +- pkg/abi/linux/ashmem.go | 2 +- pkg/abi/linux/audit.go | 2 +- pkg/abi/linux/binder.go | 2 +- pkg/abi/linux/bpf.go | 2 +- pkg/abi/linux/capability.go | 2 +- pkg/abi/linux/dev.go | 2 +- pkg/abi/linux/elf.go | 2 +- pkg/abi/linux/errors.go | 2 +- pkg/abi/linux/eventfd.go | 2 +- pkg/abi/linux/exec.go | 2 +- pkg/abi/linux/fcntl.go | 2 +- pkg/abi/linux/file.go | 2 +- pkg/abi/linux/fs.go | 2 +- pkg/abi/linux/futex.go | 2 +- pkg/abi/linux/inotify.go | 2 +- pkg/abi/linux/ioctl.go | 2 +- pkg/abi/linux/ip.go | 2 +- pkg/abi/linux/ipc.go | 2 +- pkg/abi/linux/limits.go | 2 +- pkg/abi/linux/linux.go | 2 +- pkg/abi/linux/mm.go | 2 +- pkg/abi/linux/netdevice.go | 2 +- pkg/abi/linux/netlink.go | 2 +- pkg/abi/linux/netlink_route.go | 2 +- pkg/abi/linux/poll.go | 2 +- pkg/abi/linux/prctl.go | 2 +- pkg/abi/linux/ptrace.go | 2 +- pkg/abi/linux/rusage.go | 2 +- pkg/abi/linux/sched.go | 2 +- pkg/abi/linux/seccomp.go | 2 +- pkg/abi/linux/sem.go | 2 +- pkg/abi/linux/shm.go | 2 +- pkg/abi/linux/signal.go | 2 +- pkg/abi/linux/socket.go | 2 +- pkg/abi/linux/tcp.go | 2 +- pkg/abi/linux/time.go | 2 +- pkg/abi/linux/timer.go | 2 +- pkg/abi/linux/tty.go | 2 +- pkg/abi/linux/uio.go | 2 +- pkg/abi/linux/utsname.go | 2 +- pkg/amutex/amutex.go | 2 +- pkg/amutex/amutex_test.go | 2 +- pkg/atomicbitops/atomic_bitops.go | 2 +- pkg/atomicbitops/atomic_bitops_amd64.s | 2 +- pkg/atomicbitops/atomic_bitops_common.go | 2 +- pkg/atomicbitops/atomic_bitops_test.go | 2 +- pkg/binary/binary.go | 2 +- pkg/binary/binary_test.go | 2 +- pkg/bits/bits.go | 2 +- pkg/bits/bits_template.go | 2 +- pkg/bits/uint64_arch_amd64.go | 2 +- pkg/bits/uint64_arch_amd64_asm.s | 2 +- pkg/bits/uint64_arch_generic.go | 2 +- pkg/bits/uint64_test.go | 2 +- pkg/bpf/bpf.go | 2 +- pkg/bpf/decoder.go | 2 +- pkg/bpf/decoder_test.go | 2 +- pkg/bpf/input_bytes.go | 2 +- pkg/bpf/interpreter.go | 2 +- pkg/bpf/interpreter_test.go | 2 +- pkg/bpf/program_builder.go | 2 +- pkg/bpf/program_builder_test.go | 2 +- pkg/compressio/compressio.go | 2 +- pkg/compressio/compressio_test.go | 2 +- pkg/control/client/client.go | 2 +- pkg/control/server/server.go | 2 +- pkg/cpuid/cpu_amd64.s | 2 +- pkg/cpuid/cpuid.go | 2 +- pkg/cpuid/cpuid_parse_test.go | 2 +- pkg/cpuid/cpuid_test.go | 2 +- pkg/dhcp/client.go | 2 +- pkg/dhcp/dhcp.go | 2 +- pkg/dhcp/dhcp_string.go | 2 +- pkg/dhcp/dhcp_test.go | 2 +- pkg/dhcp/server.go | 2 +- pkg/eventchannel/event.go | 2 +- pkg/eventchannel/event.proto | 2 +- pkg/fd/fd.go | 2 +- pkg/fd/fd_test.go | 2 +- pkg/fdnotifier/fdnotifier.go | 2 +- pkg/fdnotifier/poll_unsafe.go | 2 +- pkg/gate/gate.go | 2 +- pkg/gate/gate_test.go | 2 +- pkg/ilist/list.go | 2 +- pkg/ilist/list_test.go | 2 +- pkg/linewriter/linewriter.go | 2 +- pkg/linewriter/linewriter_test.go | 2 +- pkg/log/glog.go | 2 +- pkg/log/glog_unsafe.go | 2 +- pkg/log/json.go | 2 +- pkg/log/json_k8s.go | 2 +- pkg/log/json_test.go | 2 +- pkg/log/log.go | 2 +- pkg/log/log_test.go | 2 +- pkg/metric/metric.go | 2 +- pkg/metric/metric.proto | 2 +- pkg/metric/metric_test.go | 2 +- pkg/p9/buffer.go | 2 +- pkg/p9/buffer_test.go | 2 +- pkg/p9/client.go | 2 +- pkg/p9/client_file.go | 2 +- pkg/p9/client_test.go | 2 +- pkg/p9/file.go | 2 +- pkg/p9/handlers.go | 2 +- pkg/p9/local_server/local_server.go | 2 +- pkg/p9/messages.go | 2 +- pkg/p9/messages_test.go | 2 +- pkg/p9/p9.go | 2 +- pkg/p9/p9_test.go | 2 +- pkg/p9/p9test/client_test.go | 2 +- pkg/p9/p9test/p9test.go | 2 +- pkg/p9/path_tree.go | 2 +- pkg/p9/pool.go | 2 +- pkg/p9/pool_test.go | 2 +- pkg/p9/server.go | 2 +- pkg/p9/transport.go | 2 +- pkg/p9/transport_test.go | 2 +- pkg/p9/version.go | 2 +- pkg/p9/version_test.go | 2 +- pkg/rand/rand.go | 2 +- pkg/rand/rand_linux.go | 2 +- pkg/refs/refcounter.go | 2 +- pkg/refs/refcounter_state.go | 2 +- pkg/refs/refcounter_test.go | 2 +- pkg/seccomp/seccomp.go | 2 +- pkg/seccomp/seccomp_rules.go | 2 +- pkg/seccomp/seccomp_test.go | 2 +- pkg/seccomp/seccomp_test_victim.go | 2 +- pkg/seccomp/seccomp_unsafe.go | 2 +- pkg/secio/full_reader.go | 2 +- pkg/secio/secio.go | 2 +- pkg/secio/secio_test.go | 2 +- pkg/segment/range.go | 2 +- pkg/segment/set.go | 2 +- pkg/segment/set_state.go | 2 +- pkg/segment/test/segment_test.go | 2 +- pkg/segment/test/set_functions.go | 2 +- pkg/sentry/arch/aligned.go | 2 +- pkg/sentry/arch/arch.go | 2 +- pkg/sentry/arch/arch_amd64.go | 2 +- pkg/sentry/arch/arch_amd64.s | 2 +- pkg/sentry/arch/arch_state_x86.go | 2 +- pkg/sentry/arch/arch_x86.go | 2 +- pkg/sentry/arch/auxv.go | 2 +- pkg/sentry/arch/registers.proto | 2 +- pkg/sentry/arch/signal_act.go | 2 +- pkg/sentry/arch/signal_amd64.go | 2 +- pkg/sentry/arch/signal_info.go | 2 +- pkg/sentry/arch/signal_stack.go | 2 +- pkg/sentry/arch/stack.go | 2 +- pkg/sentry/arch/syscalls_amd64.go | 2 +- pkg/sentry/context/context.go | 2 +- pkg/sentry/context/contexttest/contexttest.go | 2 +- pkg/sentry/control/control.go | 2 +- pkg/sentry/control/pprof.go | 2 +- pkg/sentry/control/proc.go | 2 +- pkg/sentry/control/proc_test.go | 2 +- pkg/sentry/control/state.go | 2 +- pkg/sentry/device/device.go | 2 +- pkg/sentry/device/device_test.go | 2 +- pkg/sentry/fs/anon/anon.go | 2 +- pkg/sentry/fs/anon/device.go | 2 +- pkg/sentry/fs/ashmem/area.go | 2 +- pkg/sentry/fs/ashmem/device.go | 2 +- pkg/sentry/fs/ashmem/pin_board.go | 2 +- pkg/sentry/fs/ashmem/pin_board_test.go | 2 +- pkg/sentry/fs/attr.go | 2 +- pkg/sentry/fs/binder/binder.go | 2 +- pkg/sentry/fs/context.go | 2 +- pkg/sentry/fs/copy_up.go | 2 +- pkg/sentry/fs/copy_up_test.go | 2 +- pkg/sentry/fs/dentry.go | 2 +- pkg/sentry/fs/dev/dev.go | 2 +- pkg/sentry/fs/dev/device.go | 2 +- pkg/sentry/fs/dev/fs.go | 2 +- pkg/sentry/fs/dev/full.go | 2 +- pkg/sentry/fs/dev/null.go | 2 +- pkg/sentry/fs/dev/random.go | 2 +- pkg/sentry/fs/dirent.go | 2 +- pkg/sentry/fs/dirent_cache.go | 2 +- pkg/sentry/fs/dirent_cache_limiter.go | 2 +- pkg/sentry/fs/dirent_cache_test.go | 2 +- pkg/sentry/fs/dirent_refs_test.go | 2 +- pkg/sentry/fs/dirent_state.go | 2 +- pkg/sentry/fs/fdpipe/pipe.go | 2 +- pkg/sentry/fs/fdpipe/pipe_opener.go | 2 +- pkg/sentry/fs/fdpipe/pipe_opener_test.go | 2 +- pkg/sentry/fs/fdpipe/pipe_state.go | 2 +- pkg/sentry/fs/fdpipe/pipe_test.go | 2 +- pkg/sentry/fs/file.go | 2 +- pkg/sentry/fs/file_operations.go | 2 +- pkg/sentry/fs/file_overlay.go | 2 +- pkg/sentry/fs/file_overlay_test.go | 2 +- pkg/sentry/fs/file_state.go | 2 +- pkg/sentry/fs/file_test.go | 2 +- pkg/sentry/fs/filesystems.go | 2 +- pkg/sentry/fs/filetest/filetest.go | 2 +- pkg/sentry/fs/flags.go | 2 +- pkg/sentry/fs/fs.go | 2 +- pkg/sentry/fs/fsutil/dirty_set.go | 2 +- pkg/sentry/fs/fsutil/dirty_set_test.go | 2 +- pkg/sentry/fs/fsutil/file.go | 2 +- pkg/sentry/fs/fsutil/file_range_set.go | 2 +- pkg/sentry/fs/fsutil/frame_ref_set.go | 2 +- pkg/sentry/fs/fsutil/fsutil.go | 2 +- pkg/sentry/fs/fsutil/host_file_mapper.go | 2 +- pkg/sentry/fs/fsutil/host_file_mapper_state.go | 2 +- pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go | 2 +- pkg/sentry/fs/fsutil/host_mappable.go | 2 +- pkg/sentry/fs/fsutil/inode.go | 2 +- pkg/sentry/fs/fsutil/inode_cached.go | 2 +- pkg/sentry/fs/fsutil/inode_cached_test.go | 2 +- pkg/sentry/fs/gofer/attr.go | 2 +- pkg/sentry/fs/gofer/cache_policy.go | 2 +- pkg/sentry/fs/gofer/context_file.go | 2 +- pkg/sentry/fs/gofer/device.go | 2 +- pkg/sentry/fs/gofer/file.go | 2 +- pkg/sentry/fs/gofer/file_state.go | 2 +- pkg/sentry/fs/gofer/fs.go | 2 +- pkg/sentry/fs/gofer/gofer_test.go | 2 +- pkg/sentry/fs/gofer/handles.go | 2 +- pkg/sentry/fs/gofer/inode.go | 2 +- pkg/sentry/fs/gofer/inode_state.go | 2 +- pkg/sentry/fs/gofer/path.go | 2 +- pkg/sentry/fs/gofer/session.go | 2 +- pkg/sentry/fs/gofer/session_state.go | 2 +- pkg/sentry/fs/gofer/socket.go | 2 +- pkg/sentry/fs/gofer/util.go | 2 +- pkg/sentry/fs/host/control.go | 2 +- pkg/sentry/fs/host/descriptor.go | 2 +- pkg/sentry/fs/host/descriptor_state.go | 2 +- pkg/sentry/fs/host/descriptor_test.go | 2 +- pkg/sentry/fs/host/device.go | 2 +- pkg/sentry/fs/host/file.go | 2 +- pkg/sentry/fs/host/fs.go | 2 +- pkg/sentry/fs/host/fs_test.go | 2 +- pkg/sentry/fs/host/inode.go | 2 +- pkg/sentry/fs/host/inode_state.go | 2 +- pkg/sentry/fs/host/inode_test.go | 2 +- pkg/sentry/fs/host/ioctl_unsafe.go | 2 +- pkg/sentry/fs/host/socket.go | 2 +- pkg/sentry/fs/host/socket_iovec.go | 2 +- pkg/sentry/fs/host/socket_state.go | 2 +- pkg/sentry/fs/host/socket_test.go | 2 +- pkg/sentry/fs/host/socket_unsafe.go | 2 +- pkg/sentry/fs/host/tty.go | 2 +- pkg/sentry/fs/host/util.go | 2 +- pkg/sentry/fs/host/util_unsafe.go | 2 +- pkg/sentry/fs/host/wait_test.go | 2 +- pkg/sentry/fs/inode.go | 2 +- pkg/sentry/fs/inode_inotify.go | 2 +- pkg/sentry/fs/inode_operations.go | 2 +- pkg/sentry/fs/inode_overlay.go | 2 +- pkg/sentry/fs/inode_overlay_test.go | 2 +- pkg/sentry/fs/inotify.go | 2 +- pkg/sentry/fs/inotify_event.go | 2 +- pkg/sentry/fs/inotify_watch.go | 2 +- pkg/sentry/fs/lock/lock.go | 2 +- pkg/sentry/fs/lock/lock_range_test.go | 2 +- pkg/sentry/fs/lock/lock_set_functions.go | 2 +- pkg/sentry/fs/lock/lock_test.go | 2 +- pkg/sentry/fs/mock.go | 2 +- pkg/sentry/fs/mount.go | 2 +- pkg/sentry/fs/mount_overlay.go | 2 +- pkg/sentry/fs/mount_test.go | 2 +- pkg/sentry/fs/mounts.go | 2 +- pkg/sentry/fs/mounts_test.go | 2 +- pkg/sentry/fs/offset.go | 2 +- pkg/sentry/fs/overlay.go | 2 +- pkg/sentry/fs/path.go | 2 +- pkg/sentry/fs/path_test.go | 2 +- pkg/sentry/fs/proc/cpuinfo.go | 2 +- pkg/sentry/fs/proc/device/device.go | 2 +- pkg/sentry/fs/proc/exec_args.go | 2 +- pkg/sentry/fs/proc/fds.go | 2 +- pkg/sentry/fs/proc/filesystems.go | 2 +- pkg/sentry/fs/proc/fs.go | 2 +- pkg/sentry/fs/proc/inode.go | 2 +- pkg/sentry/fs/proc/loadavg.go | 2 +- pkg/sentry/fs/proc/meminfo.go | 2 +- pkg/sentry/fs/proc/mounts.go | 2 +- pkg/sentry/fs/proc/net.go | 2 +- pkg/sentry/fs/proc/net_test.go | 2 +- pkg/sentry/fs/proc/proc.go | 2 +- pkg/sentry/fs/proc/rpcinet_proc.go | 2 +- pkg/sentry/fs/proc/seqfile/seqfile.go | 2 +- pkg/sentry/fs/proc/seqfile/seqfile_test.go | 2 +- pkg/sentry/fs/proc/stat.go | 2 +- pkg/sentry/fs/proc/sys.go | 2 +- pkg/sentry/fs/proc/sys_net.go | 2 +- pkg/sentry/fs/proc/sys_net_state.go | 2 +- pkg/sentry/fs/proc/sys_net_test.go | 2 +- pkg/sentry/fs/proc/task.go | 2 +- pkg/sentry/fs/proc/uid_gid_map.go | 2 +- pkg/sentry/fs/proc/uptime.go | 2 +- pkg/sentry/fs/proc/version.go | 2 +- pkg/sentry/fs/ramfs/dir.go | 2 +- pkg/sentry/fs/ramfs/socket.go | 2 +- pkg/sentry/fs/ramfs/symlink.go | 2 +- pkg/sentry/fs/ramfs/tree.go | 2 +- pkg/sentry/fs/ramfs/tree_test.go | 2 +- pkg/sentry/fs/restore.go | 2 +- pkg/sentry/fs/save.go | 2 +- pkg/sentry/fs/seek.go | 2 +- pkg/sentry/fs/sync.go | 2 +- pkg/sentry/fs/sys/device.go | 2 +- pkg/sentry/fs/sys/devices.go | 2 +- pkg/sentry/fs/sys/fs.go | 2 +- pkg/sentry/fs/sys/sys.go | 2 +- pkg/sentry/fs/timerfd/timerfd.go | 2 +- pkg/sentry/fs/tmpfs/device.go | 2 +- pkg/sentry/fs/tmpfs/file_regular.go | 2 +- pkg/sentry/fs/tmpfs/file_test.go | 2 +- pkg/sentry/fs/tmpfs/fs.go | 2 +- pkg/sentry/fs/tmpfs/inode_file.go | 2 +- pkg/sentry/fs/tmpfs/tmpfs.go | 2 +- pkg/sentry/fs/tty/dir.go | 2 +- pkg/sentry/fs/tty/fs.go | 2 +- pkg/sentry/fs/tty/line_discipline.go | 2 +- pkg/sentry/fs/tty/master.go | 2 +- pkg/sentry/fs/tty/queue.go | 2 +- pkg/sentry/fs/tty/slave.go | 2 +- pkg/sentry/fs/tty/terminal.go | 2 +- pkg/sentry/fs/tty/tty_test.go | 2 +- pkg/sentry/hostcpu/getcpu_amd64.s | 2 +- pkg/sentry/hostcpu/hostcpu.go | 2 +- pkg/sentry/hostcpu/hostcpu_test.go | 2 +- pkg/sentry/inet/context.go | 2 +- pkg/sentry/inet/inet.go | 2 +- pkg/sentry/inet/test_stack.go | 2 +- pkg/sentry/kernel/abstract_socket_namespace.go | 2 +- pkg/sentry/kernel/auth/auth.go | 2 +- pkg/sentry/kernel/auth/capability_set.go | 2 +- pkg/sentry/kernel/auth/context.go | 2 +- pkg/sentry/kernel/auth/credentials.go | 2 +- pkg/sentry/kernel/auth/id.go | 2 +- pkg/sentry/kernel/auth/id_map.go | 2 +- pkg/sentry/kernel/auth/id_map_functions.go | 2 +- pkg/sentry/kernel/auth/user_namespace.go | 2 +- pkg/sentry/kernel/context.go | 2 +- pkg/sentry/kernel/contexttest/contexttest.go | 2 +- pkg/sentry/kernel/epoll/epoll.go | 2 +- pkg/sentry/kernel/epoll/epoll_state.go | 2 +- pkg/sentry/kernel/epoll/epoll_test.go | 2 +- pkg/sentry/kernel/eventfd/eventfd.go | 2 +- pkg/sentry/kernel/eventfd/eventfd_test.go | 2 +- pkg/sentry/kernel/fasync/fasync.go | 2 +- pkg/sentry/kernel/fd_map.go | 2 +- pkg/sentry/kernel/fd_map_test.go | 2 +- pkg/sentry/kernel/fs_context.go | 2 +- pkg/sentry/kernel/futex/futex.go | 2 +- pkg/sentry/kernel/futex/futex_test.go | 2 +- pkg/sentry/kernel/ipc_namespace.go | 2 +- pkg/sentry/kernel/kdefs/kdefs.go | 2 +- pkg/sentry/kernel/kernel.go | 2 +- pkg/sentry/kernel/kernel_state.go | 2 +- pkg/sentry/kernel/memevent/memory_events.go | 2 +- pkg/sentry/kernel/memevent/memory_events.proto | 2 +- pkg/sentry/kernel/pending_signals.go | 2 +- pkg/sentry/kernel/pending_signals_state.go | 2 +- pkg/sentry/kernel/pipe/buffers.go | 2 +- pkg/sentry/kernel/pipe/device.go | 2 +- pkg/sentry/kernel/pipe/node.go | 2 +- pkg/sentry/kernel/pipe/node_test.go | 2 +- pkg/sentry/kernel/pipe/pipe.go | 2 +- pkg/sentry/kernel/pipe/pipe_test.go | 2 +- pkg/sentry/kernel/pipe/reader.go | 2 +- pkg/sentry/kernel/pipe/reader_writer.go | 2 +- pkg/sentry/kernel/pipe/writer.go | 2 +- pkg/sentry/kernel/posixtimer.go | 2 +- pkg/sentry/kernel/ptrace.go | 2 +- pkg/sentry/kernel/ptrace_amd64.go | 2 +- pkg/sentry/kernel/ptrace_arm64.go | 2 +- pkg/sentry/kernel/rseq.go | 2 +- pkg/sentry/kernel/sched/cpuset.go | 2 +- pkg/sentry/kernel/sched/cpuset_test.go | 2 +- pkg/sentry/kernel/sched/sched.go | 2 +- pkg/sentry/kernel/seccomp.go | 2 +- pkg/sentry/kernel/semaphore/semaphore.go | 2 +- pkg/sentry/kernel/semaphore/semaphore_test.go | 2 +- pkg/sentry/kernel/sessions.go | 2 +- pkg/sentry/kernel/shm/device.go | 2 +- pkg/sentry/kernel/shm/shm.go | 2 +- pkg/sentry/kernel/signal.go | 2 +- pkg/sentry/kernel/signal_handlers.go | 2 +- pkg/sentry/kernel/syscalls.go | 2 +- pkg/sentry/kernel/syscalls_state.go | 2 +- pkg/sentry/kernel/syslog.go | 2 +- pkg/sentry/kernel/table_test.go | 2 +- pkg/sentry/kernel/task.go | 2 +- pkg/sentry/kernel/task_acct.go | 2 +- pkg/sentry/kernel/task_block.go | 2 +- pkg/sentry/kernel/task_clone.go | 2 +- pkg/sentry/kernel/task_context.go | 2 +- pkg/sentry/kernel/task_exec.go | 2 +- pkg/sentry/kernel/task_exit.go | 2 +- pkg/sentry/kernel/task_futex.go | 2 +- pkg/sentry/kernel/task_identity.go | 2 +- pkg/sentry/kernel/task_log.go | 2 +- pkg/sentry/kernel/task_net.go | 2 +- pkg/sentry/kernel/task_run.go | 2 +- pkg/sentry/kernel/task_sched.go | 2 +- pkg/sentry/kernel/task_signals.go | 2 +- pkg/sentry/kernel/task_start.go | 2 +- pkg/sentry/kernel/task_stop.go | 2 +- pkg/sentry/kernel/task_syscall.go | 2 +- pkg/sentry/kernel/task_test.go | 2 +- pkg/sentry/kernel/task_usermem.go | 2 +- pkg/sentry/kernel/thread_group.go | 2 +- pkg/sentry/kernel/threads.go | 2 +- pkg/sentry/kernel/time/context.go | 2 +- pkg/sentry/kernel/time/time.go | 2 +- pkg/sentry/kernel/timekeeper.go | 2 +- pkg/sentry/kernel/timekeeper_state.go | 2 +- pkg/sentry/kernel/timekeeper_test.go | 2 +- pkg/sentry/kernel/uncaught_signal.proto | 2 +- pkg/sentry/kernel/uts_namespace.go | 2 +- pkg/sentry/kernel/vdso.go | 2 +- pkg/sentry/kernel/version.go | 2 +- pkg/sentry/limits/context.go | 2 +- pkg/sentry/limits/limits.go | 2 +- pkg/sentry/limits/limits_test.go | 2 +- pkg/sentry/limits/linux.go | 2 +- pkg/sentry/loader/elf.go | 2 +- pkg/sentry/loader/interpreter.go | 2 +- pkg/sentry/loader/loader.go | 2 +- pkg/sentry/loader/vdso.go | 2 +- pkg/sentry/loader/vdso_state.go | 2 +- pkg/sentry/memmap/mapping_set.go | 2 +- pkg/sentry/memmap/mapping_set_test.go | 2 +- pkg/sentry/memmap/memmap.go | 2 +- pkg/sentry/memutil/memutil.go | 2 +- pkg/sentry/memutil/memutil_unsafe.go | 2 +- pkg/sentry/mm/address_space.go | 2 +- pkg/sentry/mm/aio_context.go | 2 +- pkg/sentry/mm/aio_context_state.go | 2 +- pkg/sentry/mm/debug.go | 2 +- pkg/sentry/mm/io.go | 2 +- pkg/sentry/mm/lifecycle.go | 2 +- pkg/sentry/mm/metadata.go | 2 +- pkg/sentry/mm/mm.go | 2 +- pkg/sentry/mm/mm_test.go | 2 +- pkg/sentry/mm/pma.go | 2 +- pkg/sentry/mm/procfs.go | 2 +- pkg/sentry/mm/save_restore.go | 2 +- pkg/sentry/mm/shm.go | 2 +- pkg/sentry/mm/special_mappable.go | 2 +- pkg/sentry/mm/syscalls.go | 2 +- pkg/sentry/mm/vma.go | 2 +- pkg/sentry/pgalloc/context.go | 2 +- pkg/sentry/pgalloc/pgalloc.go | 2 +- pkg/sentry/pgalloc/pgalloc_test.go | 2 +- pkg/sentry/pgalloc/pgalloc_unsafe.go | 2 +- pkg/sentry/pgalloc/save_restore.go | 2 +- pkg/sentry/platform/context.go | 2 +- pkg/sentry/platform/interrupt/interrupt.go | 2 +- pkg/sentry/platform/interrupt/interrupt_test.go | 2 +- pkg/sentry/platform/kvm/address_space.go | 2 +- pkg/sentry/platform/kvm/allocator.go | 2 +- pkg/sentry/platform/kvm/bluepill.go | 2 +- pkg/sentry/platform/kvm/bluepill_amd64.go | 2 +- pkg/sentry/platform/kvm/bluepill_amd64.s | 2 +- pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go | 2 +- pkg/sentry/platform/kvm/bluepill_fault.go | 2 +- pkg/sentry/platform/kvm/bluepill_unsafe.go | 2 +- pkg/sentry/platform/kvm/context.go | 2 +- pkg/sentry/platform/kvm/kvm.go | 2 +- pkg/sentry/platform/kvm/kvm_amd64.go | 2 +- pkg/sentry/platform/kvm/kvm_amd64_unsafe.go | 2 +- pkg/sentry/platform/kvm/kvm_const.go | 2 +- pkg/sentry/platform/kvm/kvm_test.go | 2 +- pkg/sentry/platform/kvm/machine.go | 2 +- pkg/sentry/platform/kvm/machine_amd64.go | 2 +- pkg/sentry/platform/kvm/machine_amd64_unsafe.go | 2 +- pkg/sentry/platform/kvm/machine_unsafe.go | 2 +- pkg/sentry/platform/kvm/physical_map.go | 2 +- pkg/sentry/platform/kvm/testutil/testutil.go | 2 +- pkg/sentry/platform/kvm/testutil/testutil_amd64.go | 2 +- pkg/sentry/platform/kvm/testutil/testutil_amd64.s | 2 +- pkg/sentry/platform/kvm/virtual_map.go | 2 +- pkg/sentry/platform/kvm/virtual_map_test.go | 2 +- pkg/sentry/platform/mmap_min_addr.go | 2 +- pkg/sentry/platform/platform.go | 2 +- pkg/sentry/platform/procid/procid.go | 2 +- pkg/sentry/platform/procid/procid_amd64.s | 2 +- pkg/sentry/platform/procid/procid_arm64.s | 2 +- pkg/sentry/platform/procid/procid_net_test.go | 2 +- pkg/sentry/platform/procid/procid_test.go | 2 +- pkg/sentry/platform/ptrace/ptrace.go | 2 +- pkg/sentry/platform/ptrace/ptrace_unsafe.go | 2 +- pkg/sentry/platform/ptrace/stub_amd64.s | 2 +- pkg/sentry/platform/ptrace/stub_unsafe.go | 2 +- pkg/sentry/platform/ptrace/subprocess.go | 2 +- pkg/sentry/platform/ptrace/subprocess_amd64.go | 2 +- pkg/sentry/platform/ptrace/subprocess_linux.go | 2 +- pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go | 2 +- pkg/sentry/platform/ptrace/subprocess_unsafe.go | 2 +- pkg/sentry/platform/ring0/defs.go | 2 +- pkg/sentry/platform/ring0/defs_amd64.go | 2 +- pkg/sentry/platform/ring0/entry_amd64.go | 2 +- pkg/sentry/platform/ring0/entry_amd64.s | 2 +- pkg/sentry/platform/ring0/gen_offsets/main.go | 2 +- pkg/sentry/platform/ring0/kernel.go | 2 +- pkg/sentry/platform/ring0/kernel_amd64.go | 2 +- pkg/sentry/platform/ring0/kernel_unsafe.go | 2 +- pkg/sentry/platform/ring0/lib_amd64.go | 2 +- pkg/sentry/platform/ring0/lib_amd64.s | 2 +- pkg/sentry/platform/ring0/offsets_amd64.go | 2 +- pkg/sentry/platform/ring0/pagetables/allocator.go | 2 +- pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_test.go | 2 +- pkg/sentry/platform/ring0/pagetables/pagetables_x86.go | 2 +- pkg/sentry/platform/ring0/pagetables/pcids_x86.go | 2 +- pkg/sentry/platform/ring0/pagetables/walker_amd64.go | 2 +- pkg/sentry/platform/ring0/ring0.go | 2 +- pkg/sentry/platform/ring0/x86.go | 2 +- pkg/sentry/platform/safecopy/atomic_amd64.s | 2 +- pkg/sentry/platform/safecopy/safecopy.go | 2 +- pkg/sentry/platform/safecopy/safecopy_test.go | 2 +- pkg/sentry/platform/safecopy/safecopy_unsafe.go | 2 +- pkg/sentry/platform/safecopy/sighandler_amd64.s | 2 +- pkg/sentry/platform/safecopy/sighandler_arm64.s | 2 +- pkg/sentry/safemem/block_unsafe.go | 2 +- pkg/sentry/safemem/io.go | 2 +- pkg/sentry/safemem/io_test.go | 2 +- pkg/sentry/safemem/safemem.go | 2 +- pkg/sentry/safemem/seq_test.go | 2 +- pkg/sentry/safemem/seq_unsafe.go | 2 +- pkg/sentry/sighandling/sighandling.go | 2 +- pkg/sentry/sighandling/sighandling_unsafe.go | 2 +- pkg/sentry/socket/control/control.go | 2 +- pkg/sentry/socket/epsocket/device.go | 2 +- pkg/sentry/socket/epsocket/epsocket.go | 2 +- pkg/sentry/socket/epsocket/provider.go | 2 +- pkg/sentry/socket/epsocket/save_restore.go | 2 +- pkg/sentry/socket/epsocket/stack.go | 2 +- pkg/sentry/socket/hostinet/device.go | 2 +- pkg/sentry/socket/hostinet/hostinet.go | 2 +- pkg/sentry/socket/hostinet/save_restore.go | 2 +- pkg/sentry/socket/hostinet/socket.go | 2 +- pkg/sentry/socket/hostinet/socket_unsafe.go | 2 +- pkg/sentry/socket/hostinet/stack.go | 2 +- pkg/sentry/socket/netlink/message.go | 2 +- pkg/sentry/socket/netlink/port/port.go | 2 +- pkg/sentry/socket/netlink/port/port_test.go | 2 +- pkg/sentry/socket/netlink/provider.go | 2 +- pkg/sentry/socket/netlink/route/protocol.go | 2 +- pkg/sentry/socket/netlink/socket.go | 2 +- pkg/sentry/socket/rpcinet/conn/conn.go | 2 +- pkg/sentry/socket/rpcinet/device.go | 2 +- pkg/sentry/socket/rpcinet/notifier/notifier.go | 2 +- pkg/sentry/socket/rpcinet/rpcinet.go | 2 +- pkg/sentry/socket/rpcinet/socket.go | 2 +- pkg/sentry/socket/rpcinet/stack.go | 2 +- pkg/sentry/socket/rpcinet/stack_unsafe.go | 2 +- pkg/sentry/socket/socket.go | 2 +- pkg/sentry/socket/unix/device.go | 2 +- pkg/sentry/socket/unix/io.go | 2 +- pkg/sentry/socket/unix/transport/connectioned.go | 2 +- pkg/sentry/socket/unix/transport/connectioned_state.go | 2 +- pkg/sentry/socket/unix/transport/connectionless.go | 2 +- pkg/sentry/socket/unix/transport/queue.go | 2 +- pkg/sentry/socket/unix/transport/unix.go | 2 +- pkg/sentry/socket/unix/unix.go | 2 +- pkg/sentry/state/state.go | 2 +- pkg/sentry/state/state_metadata.go | 2 +- pkg/sentry/state/state_unsafe.go | 2 +- pkg/sentry/strace/capability.go | 2 +- pkg/sentry/strace/clone.go | 2 +- pkg/sentry/strace/futex.go | 2 +- pkg/sentry/strace/linux64.go | 2 +- pkg/sentry/strace/open.go | 2 +- pkg/sentry/strace/poll.go | 2 +- pkg/sentry/strace/ptrace.go | 2 +- pkg/sentry/strace/signal.go | 2 +- pkg/sentry/strace/socket.go | 2 +- pkg/sentry/strace/strace.go | 2 +- pkg/sentry/strace/strace.proto | 2 +- pkg/sentry/strace/syscalls.go | 2 +- pkg/sentry/syscalls/epoll.go | 2 +- pkg/sentry/syscalls/linux/error.go | 2 +- pkg/sentry/syscalls/linux/flags.go | 2 +- pkg/sentry/syscalls/linux/linux64.go | 2 +- pkg/sentry/syscalls/linux/sigset.go | 2 +- pkg/sentry/syscalls/linux/sys_aio.go | 2 +- pkg/sentry/syscalls/linux/sys_capability.go | 2 +- pkg/sentry/syscalls/linux/sys_epoll.go | 2 +- pkg/sentry/syscalls/linux/sys_eventfd.go | 2 +- pkg/sentry/syscalls/linux/sys_file.go | 2 +- pkg/sentry/syscalls/linux/sys_futex.go | 2 +- pkg/sentry/syscalls/linux/sys_getdents.go | 2 +- pkg/sentry/syscalls/linux/sys_identity.go | 2 +- pkg/sentry/syscalls/linux/sys_inotify.go | 2 +- pkg/sentry/syscalls/linux/sys_lseek.go | 2 +- pkg/sentry/syscalls/linux/sys_mmap.go | 2 +- pkg/sentry/syscalls/linux/sys_mount.go | 2 +- pkg/sentry/syscalls/linux/sys_pipe.go | 2 +- pkg/sentry/syscalls/linux/sys_poll.go | 2 +- pkg/sentry/syscalls/linux/sys_prctl.go | 2 +- pkg/sentry/syscalls/linux/sys_random.go | 2 +- pkg/sentry/syscalls/linux/sys_read.go | 2 +- pkg/sentry/syscalls/linux/sys_rlimit.go | 2 +- pkg/sentry/syscalls/linux/sys_rusage.go | 2 +- pkg/sentry/syscalls/linux/sys_sched.go | 2 +- pkg/sentry/syscalls/linux/sys_seccomp.go | 2 +- pkg/sentry/syscalls/linux/sys_sem.go | 2 +- pkg/sentry/syscalls/linux/sys_shm.go | 2 +- pkg/sentry/syscalls/linux/sys_signal.go | 2 +- pkg/sentry/syscalls/linux/sys_socket.go | 2 +- pkg/sentry/syscalls/linux/sys_stat.go | 2 +- pkg/sentry/syscalls/linux/sys_sync.go | 2 +- pkg/sentry/syscalls/linux/sys_sysinfo.go | 2 +- pkg/sentry/syscalls/linux/sys_syslog.go | 2 +- pkg/sentry/syscalls/linux/sys_thread.go | 2 +- pkg/sentry/syscalls/linux/sys_time.go | 2 +- pkg/sentry/syscalls/linux/sys_timer.go | 2 +- pkg/sentry/syscalls/linux/sys_timerfd.go | 2 +- pkg/sentry/syscalls/linux/sys_tls.go | 2 +- pkg/sentry/syscalls/linux/sys_utsname.go | 2 +- pkg/sentry/syscalls/linux/sys_write.go | 2 +- pkg/sentry/syscalls/linux/timespec.go | 2 +- pkg/sentry/syscalls/syscalls.go | 2 +- pkg/sentry/time/calibrated_clock.go | 2 +- pkg/sentry/time/calibrated_clock_test.go | 2 +- pkg/sentry/time/clock_id.go | 2 +- pkg/sentry/time/clocks.go | 2 +- pkg/sentry/time/muldiv_amd64.s | 2 +- pkg/sentry/time/muldiv_arm64.s | 2 +- pkg/sentry/time/parameters.go | 2 +- pkg/sentry/time/parameters_test.go | 2 +- pkg/sentry/time/sampler.go | 2 +- pkg/sentry/time/sampler_test.go | 2 +- pkg/sentry/time/sampler_unsafe.go | 2 +- pkg/sentry/time/tsc_amd64.s | 2 +- pkg/sentry/time/tsc_arm64.s | 2 +- pkg/sentry/unimpl/events.go | 2 +- pkg/sentry/unimpl/unimplemented_syscall.proto | 2 +- pkg/sentry/uniqueid/context.go | 2 +- pkg/sentry/usage/cpu.go | 2 +- pkg/sentry/usage/io.go | 2 +- pkg/sentry/usage/memory.go | 2 +- pkg/sentry/usage/memory_unsafe.go | 2 +- pkg/sentry/usage/usage.go | 2 +- pkg/sentry/usermem/access_type.go | 2 +- pkg/sentry/usermem/addr.go | 2 +- pkg/sentry/usermem/addr_range_seq_test.go | 2 +- pkg/sentry/usermem/addr_range_seq_unsafe.go | 2 +- pkg/sentry/usermem/bytes_io.go | 2 +- pkg/sentry/usermem/bytes_io_unsafe.go | 2 +- pkg/sentry/usermem/usermem.go | 2 +- pkg/sentry/usermem/usermem_arm64.go | 2 +- pkg/sentry/usermem/usermem_test.go | 2 +- pkg/sentry/usermem/usermem_unsafe.go | 2 +- pkg/sentry/usermem/usermem_x86.go | 2 +- pkg/sentry/watchdog/watchdog.go | 2 +- pkg/sleep/commit_amd64.s | 2 +- pkg/sleep/commit_asm.go | 2 +- pkg/sleep/commit_noasm.go | 2 +- pkg/sleep/empty.s | 2 +- pkg/sleep/sleep_test.go | 2 +- pkg/sleep/sleep_unsafe.go | 2 +- pkg/state/decode.go | 2 +- pkg/state/encode.go | 2 +- pkg/state/encode_unsafe.go | 2 +- pkg/state/map.go | 2 +- pkg/state/object.proto | 2 +- pkg/state/printer.go | 2 +- pkg/state/state.go | 2 +- pkg/state/state_test.go | 2 +- pkg/state/statefile/statefile.go | 2 +- pkg/state/statefile/statefile_test.go | 2 +- pkg/state/stats.go | 2 +- pkg/syserr/host_linux.go | 2 +- pkg/syserr/netstack.go | 2 +- pkg/syserr/syserr.go | 2 +- pkg/syserror/syserror.go | 2 +- pkg/syserror/syserror_test.go | 2 +- pkg/tcpip/adapters/gonet/gonet.go | 2 +- pkg/tcpip/adapters/gonet/gonet_test.go | 2 +- pkg/tcpip/buffer/prependable.go | 2 +- pkg/tcpip/buffer/view.go | 2 +- pkg/tcpip/buffer/view_test.go | 2 +- pkg/tcpip/checker/checker.go | 2 +- pkg/tcpip/hash/jenkins/jenkins.go | 2 +- pkg/tcpip/hash/jenkins/jenkins_test.go | 2 +- pkg/tcpip/header/arp.go | 2 +- pkg/tcpip/header/checksum.go | 2 +- pkg/tcpip/header/eth.go | 2 +- pkg/tcpip/header/gue.go | 2 +- pkg/tcpip/header/icmpv4.go | 2 +- pkg/tcpip/header/icmpv6.go | 2 +- pkg/tcpip/header/interfaces.go | 2 +- pkg/tcpip/header/ipv4.go | 2 +- pkg/tcpip/header/ipv6.go | 2 +- pkg/tcpip/header/ipv6_fragment.go | 2 +- pkg/tcpip/header/ipversion_test.go | 2 +- pkg/tcpip/header/tcp.go | 2 +- pkg/tcpip/header/tcp_test.go | 2 +- pkg/tcpip/header/udp.go | 2 +- pkg/tcpip/link/channel/channel.go | 2 +- pkg/tcpip/link/fdbased/endpoint.go | 2 +- pkg/tcpip/link/fdbased/endpoint_test.go | 2 +- pkg/tcpip/link/fdbased/endpoint_unsafe.go | 2 +- pkg/tcpip/link/fdbased/mmap.go | 2 +- pkg/tcpip/link/fdbased/mmap_amd64_unsafe.go | 2 +- pkg/tcpip/link/loopback/loopback.go | 2 +- pkg/tcpip/link/muxed/injectable.go | 2 +- pkg/tcpip/link/muxed/injectable_test.go | 2 +- pkg/tcpip/link/rawfile/blockingpoll_amd64.s | 2 +- pkg/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go | 2 +- pkg/tcpip/link/rawfile/blockingpoll_unsafe.go | 2 +- pkg/tcpip/link/rawfile/errors.go | 2 +- pkg/tcpip/link/rawfile/rawfile_unsafe.go | 2 +- pkg/tcpip/link/sharedmem/pipe/pipe.go | 2 +- pkg/tcpip/link/sharedmem/pipe/pipe_test.go | 2 +- pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go | 2 +- pkg/tcpip/link/sharedmem/pipe/rx.go | 2 +- pkg/tcpip/link/sharedmem/pipe/tx.go | 2 +- pkg/tcpip/link/sharedmem/queue/queue_test.go | 2 +- pkg/tcpip/link/sharedmem/queue/rx.go | 2 +- pkg/tcpip/link/sharedmem/queue/tx.go | 2 +- pkg/tcpip/link/sharedmem/rx.go | 2 +- pkg/tcpip/link/sharedmem/sharedmem.go | 2 +- pkg/tcpip/link/sharedmem/sharedmem_test.go | 2 +- pkg/tcpip/link/sharedmem/sharedmem_unsafe.go | 2 +- pkg/tcpip/link/sharedmem/tx.go | 2 +- pkg/tcpip/link/sniffer/pcap.go | 2 +- pkg/tcpip/link/sniffer/sniffer.go | 2 +- pkg/tcpip/link/tun/tun_unsafe.go | 2 +- pkg/tcpip/link/waitable/waitable.go | 2 +- pkg/tcpip/link/waitable/waitable_test.go | 2 +- pkg/tcpip/network/arp/arp.go | 2 +- pkg/tcpip/network/arp/arp_test.go | 2 +- pkg/tcpip/network/fragmentation/frag_heap.go | 2 +- pkg/tcpip/network/fragmentation/frag_heap_test.go | 2 +- pkg/tcpip/network/fragmentation/fragmentation.go | 2 +- pkg/tcpip/network/fragmentation/fragmentation_test.go | 2 +- pkg/tcpip/network/fragmentation/reassembler.go | 2 +- pkg/tcpip/network/fragmentation/reassembler_test.go | 2 +- pkg/tcpip/network/hash/hash.go | 2 +- pkg/tcpip/network/ip_test.go | 2 +- pkg/tcpip/network/ipv4/icmp.go | 2 +- pkg/tcpip/network/ipv4/ipv4.go | 2 +- pkg/tcpip/network/ipv4/ipv4_test.go | 2 +- pkg/tcpip/network/ipv6/icmp.go | 2 +- pkg/tcpip/network/ipv6/icmp_test.go | 2 +- pkg/tcpip/network/ipv6/ipv6.go | 2 +- pkg/tcpip/ports/ports.go | 2 +- pkg/tcpip/ports/ports_test.go | 2 +- pkg/tcpip/sample/tun_tcp_connect/main.go | 2 +- pkg/tcpip/sample/tun_tcp_echo/main.go | 2 +- pkg/tcpip/seqnum/seqnum.go | 2 +- pkg/tcpip/stack/linkaddrcache.go | 2 +- pkg/tcpip/stack/linkaddrcache_test.go | 2 +- pkg/tcpip/stack/nic.go | 2 +- pkg/tcpip/stack/registration.go | 2 +- pkg/tcpip/stack/route.go | 2 +- pkg/tcpip/stack/stack.go | 2 +- pkg/tcpip/stack/stack_global_state.go | 2 +- pkg/tcpip/stack/stack_test.go | 2 +- pkg/tcpip/stack/transport_demuxer.go | 2 +- pkg/tcpip/stack/transport_test.go | 2 +- pkg/tcpip/tcpip.go | 2 +- pkg/tcpip/tcpip_test.go | 2 +- pkg/tcpip/time.s | 2 +- pkg/tcpip/time_unsafe.go | 2 +- pkg/tcpip/transport/icmp/endpoint.go | 2 +- pkg/tcpip/transport/icmp/endpoint_state.go | 2 +- pkg/tcpip/transport/icmp/protocol.go | 2 +- pkg/tcpip/transport/raw/raw.go | 2 +- pkg/tcpip/transport/raw/state.go | 2 +- pkg/tcpip/transport/tcp/accept.go | 2 +- pkg/tcpip/transport/tcp/connect.go | 2 +- pkg/tcpip/transport/tcp/cubic.go | 2 +- pkg/tcpip/transport/tcp/dual_stack_test.go | 2 +- pkg/tcpip/transport/tcp/endpoint.go | 2 +- pkg/tcpip/transport/tcp/endpoint_state.go | 2 +- pkg/tcpip/transport/tcp/forwarder.go | 2 +- pkg/tcpip/transport/tcp/protocol.go | 2 +- pkg/tcpip/transport/tcp/rcv.go | 2 +- pkg/tcpip/transport/tcp/reno.go | 2 +- pkg/tcpip/transport/tcp/sack.go | 2 +- pkg/tcpip/transport/tcp/sack_scoreboard.go | 2 +- pkg/tcpip/transport/tcp/sack_scoreboard_test.go | 2 +- pkg/tcpip/transport/tcp/segment.go | 2 +- pkg/tcpip/transport/tcp/segment_heap.go | 2 +- pkg/tcpip/transport/tcp/segment_queue.go | 2 +- pkg/tcpip/transport/tcp/segment_state.go | 2 +- pkg/tcpip/transport/tcp/snd.go | 2 +- pkg/tcpip/transport/tcp/snd_state.go | 2 +- pkg/tcpip/transport/tcp/tcp_sack_test.go | 2 +- pkg/tcpip/transport/tcp/tcp_test.go | 2 +- pkg/tcpip/transport/tcp/tcp_timestamp_test.go | 2 +- pkg/tcpip/transport/tcp/testing/context/context.go | 2 +- pkg/tcpip/transport/tcp/timer.go | 2 +- pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go | 2 +- pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go | 2 +- pkg/tcpip/transport/udp/endpoint.go | 2 +- pkg/tcpip/transport/udp/endpoint_state.go | 2 +- pkg/tcpip/transport/udp/forwarder.go | 2 +- pkg/tcpip/transport/udp/protocol.go | 2 +- pkg/tcpip/transport/udp/udp_test.go | 2 +- pkg/tmutex/tmutex.go | 2 +- pkg/tmutex/tmutex_test.go | 2 +- pkg/unet/unet.go | 2 +- pkg/unet/unet_test.go | 2 +- pkg/unet/unet_unsafe.go | 2 +- pkg/urpc/urpc.go | 2 +- pkg/urpc/urpc_test.go | 2 +- pkg/waiter/waiter.go | 2 +- pkg/waiter/waiter_test.go | 2 +- runsc/boot/compat.go | 2 +- runsc/boot/compat_amd64.go | 2 +- runsc/boot/compat_test.go | 2 +- runsc/boot/config.go | 2 +- runsc/boot/controller.go | 2 +- runsc/boot/debug.go | 2 +- runsc/boot/events.go | 2 +- runsc/boot/fds.go | 2 +- runsc/boot/filter/config.go | 2 +- runsc/boot/filter/extra_filters.go | 2 +- runsc/boot/filter/extra_filters_msan.go | 2 +- runsc/boot/filter/extra_filters_race.go | 2 +- runsc/boot/filter/filter.go | 2 +- runsc/boot/fs.go | 2 +- runsc/boot/limits.go | 2 +- runsc/boot/loader.go | 2 +- runsc/boot/loader_test.go | 2 +- runsc/boot/network.go | 2 +- runsc/boot/strace.go | 2 +- runsc/cgroup/cgroup.go | 2 +- runsc/cgroup/cgroup_test.go | 2 +- runsc/cmd/boot.go | 2 +- runsc/cmd/capability.go | 2 +- runsc/cmd/capability_test.go | 2 +- runsc/cmd/checkpoint.go | 2 +- runsc/cmd/chroot.go | 2 +- runsc/cmd/cmd.go | 2 +- runsc/cmd/create.go | 2 +- runsc/cmd/debug.go | 2 +- runsc/cmd/delete.go | 2 +- runsc/cmd/delete_test.go | 2 +- runsc/cmd/do.go | 2 +- runsc/cmd/events.go | 2 +- runsc/cmd/exec.go | 2 +- runsc/cmd/exec_test.go | 2 +- runsc/cmd/gofer.go | 2 +- runsc/cmd/gofer_test.go | 2 +- runsc/cmd/kill.go | 2 +- runsc/cmd/list.go | 2 +- runsc/cmd/path.go | 2 +- runsc/cmd/pause.go | 2 +- runsc/cmd/ps.go | 2 +- runsc/cmd/restore.go | 2 +- runsc/cmd/resume.go | 2 +- runsc/cmd/run.go | 2 +- runsc/cmd/spec.go | 2 +- runsc/cmd/start.go | 2 +- runsc/cmd/state.go | 2 +- runsc/cmd/wait.go | 2 +- runsc/console/console.go | 2 +- runsc/container/console_test.go | 2 +- runsc/container/container.go | 2 +- runsc/container/container_test.go | 2 +- runsc/container/hook.go | 2 +- runsc/container/multi_container_test.go | 2 +- runsc/container/shared_volume_test.go | 2 +- runsc/container/status.go | 2 +- runsc/container/test_app.go | 2 +- runsc/fsgofer/filter/config.go | 2 +- runsc/fsgofer/filter/extra_filters.go | 2 +- runsc/fsgofer/filter/extra_filters_msan.go | 2 +- runsc/fsgofer/filter/extra_filters_race.go | 2 +- runsc/fsgofer/filter/filter.go | 2 +- runsc/fsgofer/fsgofer.go | 2 +- runsc/fsgofer/fsgofer_test.go | 2 +- runsc/fsgofer/fsgofer_unsafe.go | 2 +- runsc/main.go | 2 +- runsc/sandbox/network.go | 2 +- runsc/sandbox/network_unsafe.go | 2 +- runsc/sandbox/sandbox.go | 2 +- runsc/specutils/fs.go | 2 +- runsc/specutils/namespace.go | 2 +- runsc/specutils/specutils.go | 2 +- runsc/specutils/specutils_test.go | 2 +- runsc/test/image/image.go | 2 +- runsc/test/image/image_test.go | 2 +- runsc/test/image/mysql.sql | 2 +- runsc/test/image/ruby.rb | 2 +- runsc/test/image/ruby.sh | 2 +- runsc/test/install.sh | 2 +- runsc/test/integration/exec_test.go | 2 +- runsc/test/integration/integration.go | 2 +- runsc/test/integration/integration_test.go | 2 +- runsc/test/root/cgroup_test.go | 2 +- runsc/test/root/chroot_test.go | 2 +- runsc/test/root/crictl_test.go | 2 +- runsc/test/root/root.go | 2 +- runsc/test/root/testdata/busybox.go | 2 +- runsc/test/root/testdata/containerd_config.go | 2 +- runsc/test/root/testdata/httpd.go | 2 +- runsc/test/root/testdata/httpd_mount_paths.go | 2 +- runsc/test/root/testdata/sandbox.go | 2 +- runsc/test/testutil/crictl.go | 2 +- runsc/test/testutil/docker.go | 2 +- runsc/test/testutil/testutil.go | 2 +- runsc/test/testutil/testutil_race.go | 2 +- runsc/tools/dockercfg/dockercfg.go | 2 +- runsc/version.go | 2 +- test/syscalls/gtest/gtest.go | 2 +- test/syscalls/linux/32bit.cc | 2 +- test/syscalls/linux/accept_bind.cc | 2 +- test/syscalls/linux/accept_bind_stream.cc | 2 +- test/syscalls/linux/access.cc | 2 +- test/syscalls/linux/affinity.cc | 2 +- test/syscalls/linux/aio.cc | 2 +- test/syscalls/linux/alarm.cc | 2 +- test/syscalls/linux/arch_prctl.cc | 2 +- test/syscalls/linux/bad.cc | 2 +- test/syscalls/linux/base_poll_test.cc | 2 +- test/syscalls/linux/base_poll_test.h | 2 +- test/syscalls/linux/bind.cc | 2 +- test/syscalls/linux/brk.cc | 2 +- test/syscalls/linux/chdir.cc | 2 +- test/syscalls/linux/chmod.cc | 2 +- test/syscalls/linux/chown.cc | 2 +- test/syscalls/linux/chroot.cc | 2 +- test/syscalls/linux/clock_getres.cc | 2 +- test/syscalls/linux/clock_gettime.cc | 2 +- test/syscalls/linux/clock_nanosleep.cc | 2 +- test/syscalls/linux/concurrency.cc | 2 +- test/syscalls/linux/creat.cc | 2 +- test/syscalls/linux/dev.cc | 2 +- test/syscalls/linux/dup.cc | 2 +- test/syscalls/linux/epoll.cc | 2 +- test/syscalls/linux/eventfd.cc | 2 +- test/syscalls/linux/exceptions.cc | 2 +- test/syscalls/linux/exec.cc | 2 +- test/syscalls/linux/exec.h | 2 +- test/syscalls/linux/exec_assert_closed_workload.cc | 2 +- test/syscalls/linux/exec_basic_workload.cc | 2 +- test/syscalls/linux/exec_binary.cc | 2 +- test/syscalls/linux/exec_proc_exe_workload.cc | 2 +- test/syscalls/linux/exec_state_workload.cc | 2 +- test/syscalls/linux/exit.cc | 2 +- test/syscalls/linux/exit_script.sh | 2 +- test/syscalls/linux/fadvise64.cc | 2 +- test/syscalls/linux/fallocate.cc | 2 +- test/syscalls/linux/fault.cc | 2 +- test/syscalls/linux/fchdir.cc | 2 +- test/syscalls/linux/fcntl.cc | 2 +- test/syscalls/linux/file_base.h | 2 +- test/syscalls/linux/flock.cc | 2 +- test/syscalls/linux/fork.cc | 2 +- test/syscalls/linux/fpsig_fork.cc | 2 +- test/syscalls/linux/fpsig_nested.cc | 2 +- test/syscalls/linux/fsync.cc | 2 +- test/syscalls/linux/futex.cc | 2 +- test/syscalls/linux/getcpu.cc | 2 +- test/syscalls/linux/getdents.cc | 2 +- test/syscalls/linux/getrandom.cc | 2 +- test/syscalls/linux/getrusage.cc | 2 +- test/syscalls/linux/inotify.cc | 2 +- test/syscalls/linux/ioctl.cc | 2 +- test/syscalls/linux/ip_socket_test_util.cc | 2 +- test/syscalls/linux/ip_socket_test_util.h | 2 +- test/syscalls/linux/itimer.cc | 2 +- test/syscalls/linux/kill.cc | 2 +- test/syscalls/linux/link.cc | 2 +- test/syscalls/linux/lseek.cc | 2 +- test/syscalls/linux/madvise.cc | 2 +- test/syscalls/linux/memfd.cc | 2 +- test/syscalls/linux/memory_accounting.cc | 2 +- test/syscalls/linux/mempolicy.cc | 2 +- test/syscalls/linux/mincore.cc | 2 +- test/syscalls/linux/mkdir.cc | 2 +- test/syscalls/linux/mknod.cc | 2 +- test/syscalls/linux/mlock.cc | 2 +- test/syscalls/linux/mmap.cc | 2 +- test/syscalls/linux/mount.cc | 2 +- test/syscalls/linux/mremap.cc | 2 +- test/syscalls/linux/msync.cc | 2 +- test/syscalls/linux/munmap.cc | 2 +- test/syscalls/linux/open.cc | 2 +- test/syscalls/linux/open_create.cc | 2 +- test/syscalls/linux/partial_bad_buffer.cc | 2 +- test/syscalls/linux/pause.cc | 2 +- test/syscalls/linux/pipe.cc | 2 +- test/syscalls/linux/poll.cc | 2 +- test/syscalls/linux/ppoll.cc | 2 +- test/syscalls/linux/prctl.cc | 2 +- test/syscalls/linux/prctl_setuid.cc | 2 +- test/syscalls/linux/pread64.cc | 2 +- test/syscalls/linux/preadv.cc | 2 +- test/syscalls/linux/preadv2.cc | 2 +- test/syscalls/linux/priority.cc | 2 +- test/syscalls/linux/priority_execve.cc | 2 +- test/syscalls/linux/proc.cc | 2 +- test/syscalls/linux/proc_net.cc | 2 +- test/syscalls/linux/proc_net_unix.cc | 2 +- test/syscalls/linux/proc_pid_smaps.cc | 2 +- test/syscalls/linux/proc_pid_uid_gid_map.cc | 2 +- test/syscalls/linux/pselect.cc | 2 +- test/syscalls/linux/ptrace.cc | 2 +- test/syscalls/linux/pty.cc | 2 +- test/syscalls/linux/pwrite64.cc | 2 +- test/syscalls/linux/pwritev2.cc | 2 +- test/syscalls/linux/raw_socket_ipv4.cc | 2 +- test/syscalls/linux/read.cc | 2 +- test/syscalls/linux/readv.cc | 2 +- test/syscalls/linux/readv_common.cc | 2 +- test/syscalls/linux/readv_common.h | 2 +- test/syscalls/linux/readv_socket.cc | 2 +- test/syscalls/linux/rename.cc | 2 +- test/syscalls/linux/rlimits.cc | 2 +- test/syscalls/linux/rtsignal.cc | 2 +- test/syscalls/linux/sched.cc | 2 +- test/syscalls/linux/sched_yield.cc | 2 +- test/syscalls/linux/seccomp.cc | 2 +- test/syscalls/linux/select.cc | 2 +- test/syscalls/linux/semaphore.cc | 2 +- test/syscalls/linux/sendfile.cc | 2 +- test/syscalls/linux/sendfile_socket.cc | 2 +- test/syscalls/linux/shm.cc | 2 +- test/syscalls/linux/sigaction.cc | 2 +- test/syscalls/linux/sigaltstack.cc | 2 +- test/syscalls/linux/sigaltstack_check.cc | 2 +- test/syscalls/linux/sigiret.cc | 2 +- test/syscalls/linux/sigprocmask.cc | 2 +- test/syscalls/linux/sigstop.cc | 2 +- test/syscalls/linux/sigtimedwait.cc | 2 +- test/syscalls/linux/socket_abstract.cc | 2 +- test/syscalls/linux/socket_blocking.cc | 2 +- test/syscalls/linux/socket_blocking.h | 2 +- test/syscalls/linux/socket_filesystem.cc | 2 +- test/syscalls/linux/socket_generic.cc | 2 +- test/syscalls/linux/socket_generic.h | 2 +- test/syscalls/linux/socket_inet_loopback.cc | 2 +- test/syscalls/linux/socket_ip_loopback_blocking.cc | 2 +- test/syscalls/linux/socket_ip_tcp_generic.cc | 2 +- test/syscalls/linux/socket_ip_tcp_generic.h | 2 +- test/syscalls/linux/socket_ip_tcp_generic_loopback.cc | 2 +- test/syscalls/linux/socket_ip_tcp_loopback.cc | 2 +- test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc | 2 +- test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc | 2 +- test/syscalls/linux/socket_ip_tcp_udp_generic.cc | 2 +- test/syscalls/linux/socket_ip_udp_generic.cc | 2 +- test/syscalls/linux/socket_ip_udp_generic.h | 2 +- test/syscalls/linux/socket_ip_udp_loopback.cc | 2 +- test/syscalls/linux/socket_ip_udp_loopback_blocking.cc | 2 +- test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc | 2 +- .../syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc | 2 +- test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h | 2 +- .../linux/socket_ipv4_tcp_unbound_external_networking_test.cc | 2 +- test/syscalls/linux/socket_ipv4_udp_unbound.cc | 2 +- test/syscalls/linux/socket_ipv4_udp_unbound.h | 2 +- .../syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc | 2 +- test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h | 2 +- .../linux/socket_ipv4_udp_unbound_external_networking_test.cc | 2 +- test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc | 2 +- test/syscalls/linux/socket_netdevice.cc | 2 +- test/syscalls/linux/socket_netlink_route.cc | 2 +- test/syscalls/linux/socket_netlink_util.cc | 2 +- test/syscalls/linux/socket_netlink_util.h | 2 +- test/syscalls/linux/socket_non_blocking.cc | 2 +- test/syscalls/linux/socket_non_blocking.h | 2 +- test/syscalls/linux/socket_non_stream.cc | 2 +- test/syscalls/linux/socket_non_stream.h | 2 +- test/syscalls/linux/socket_non_stream_blocking.cc | 2 +- test/syscalls/linux/socket_non_stream_blocking.h | 2 +- test/syscalls/linux/socket_stream.cc | 2 +- test/syscalls/linux/socket_stream.h | 2 +- test/syscalls/linux/socket_stream_blocking.cc | 2 +- test/syscalls/linux/socket_stream_blocking.h | 2 +- test/syscalls/linux/socket_stream_nonblock.cc | 2 +- test/syscalls/linux/socket_stream_nonblock.h | 2 +- test/syscalls/linux/socket_test_util.cc | 2 +- test/syscalls/linux/socket_test_util.h | 2 +- test/syscalls/linux/socket_unix.cc | 2 +- test/syscalls/linux/socket_unix.h | 2 +- test/syscalls/linux/socket_unix_abstract.cc | 2 +- test/syscalls/linux/socket_unix_abstract_nonblock.cc | 2 +- test/syscalls/linux/socket_unix_blocking_local.cc | 2 +- test/syscalls/linux/socket_unix_dgram.cc | 2 +- test/syscalls/linux/socket_unix_dgram.h | 2 +- test/syscalls/linux/socket_unix_dgram_local.cc | 2 +- test/syscalls/linux/socket_unix_dgram_non_blocking.cc | 2 +- test/syscalls/linux/socket_unix_domain.cc | 2 +- test/syscalls/linux/socket_unix_filesystem.cc | 2 +- test/syscalls/linux/socket_unix_filesystem_nonblock.cc | 2 +- test/syscalls/linux/socket_unix_non_stream.cc | 2 +- test/syscalls/linux/socket_unix_non_stream.h | 2 +- test/syscalls/linux/socket_unix_non_stream_blocking_local.cc | 2 +- test/syscalls/linux/socket_unix_pair.cc | 2 +- test/syscalls/linux/socket_unix_pair_nonblock.cc | 2 +- test/syscalls/linux/socket_unix_seqpacket.cc | 2 +- test/syscalls/linux/socket_unix_seqpacket.h | 2 +- test/syscalls/linux/socket_unix_seqpacket_local.cc | 2 +- test/syscalls/linux/socket_unix_stream.cc | 2 +- test/syscalls/linux/socket_unix_stream_blocking_local.cc | 2 +- test/syscalls/linux/socket_unix_stream_local.cc | 2 +- test/syscalls/linux/socket_unix_stream_nonblock_local.cc | 2 +- test/syscalls/linux/socket_unix_unbound_abstract.cc | 2 +- test/syscalls/linux/socket_unix_unbound_dgram.cc | 2 +- test/syscalls/linux/socket_unix_unbound_filesystem.cc | 2 +- test/syscalls/linux/socket_unix_unbound_seqpacket.cc | 2 +- test/syscalls/linux/socket_unix_unbound_stream.cc | 2 +- test/syscalls/linux/stat.cc | 2 +- test/syscalls/linux/stat_times.cc | 2 +- test/syscalls/linux/statfs.cc | 2 +- test/syscalls/linux/sticky.cc | 2 +- test/syscalls/linux/symlink.cc | 2 +- test/syscalls/linux/sync.cc | 2 +- test/syscalls/linux/sync_file_range.cc | 2 +- test/syscalls/linux/sysinfo.cc | 2 +- test/syscalls/linux/syslog.cc | 2 +- test/syscalls/linux/sysret.cc | 2 +- test/syscalls/linux/tcp_socket.cc | 2 +- test/syscalls/linux/temp_umask.h | 2 +- test/syscalls/linux/tgkill.cc | 2 +- test/syscalls/linux/time.cc | 2 +- test/syscalls/linux/timerfd.cc | 2 +- test/syscalls/linux/timers.cc | 2 +- test/syscalls/linux/tkill.cc | 2 +- test/syscalls/linux/truncate.cc | 2 +- test/syscalls/linux/udp_bind.cc | 2 +- test/syscalls/linux/udp_socket.cc | 2 +- test/syscalls/linux/uidgid.cc | 2 +- test/syscalls/linux/uname.cc | 2 +- test/syscalls/linux/unix_domain_socket_test_util.cc | 2 +- test/syscalls/linux/unix_domain_socket_test_util.h | 2 +- test/syscalls/linux/unlink.cc | 2 +- test/syscalls/linux/unshare.cc | 2 +- test/syscalls/linux/utimes.cc | 2 +- test/syscalls/linux/vdso.cc | 2 +- test/syscalls/linux/vdso_clock_gettime.cc | 2 +- test/syscalls/linux/vfork.cc | 2 +- test/syscalls/linux/vsyscall.cc | 2 +- test/syscalls/linux/wait.cc | 2 +- test/syscalls/linux/write.cc | 2 +- test/syscalls/syscall_test_runner.go | 2 +- test/syscalls/syscall_test_runner.sh | 2 +- test/util/capability_util.cc | 2 +- test/util/capability_util.h | 2 +- test/util/cleanup.h | 2 +- test/util/epoll_util.cc | 2 +- test/util/epoll_util.h | 2 +- test/util/eventfd_util.h | 2 +- test/util/file_descriptor.h | 2 +- test/util/fs_util.cc | 2 +- test/util/fs_util.h | 2 +- test/util/fs_util_test.cc | 2 +- test/util/logging.cc | 2 +- test/util/logging.h | 2 +- test/util/memory_util.h | 2 +- test/util/mount_util.h | 2 +- test/util/multiprocess_util.cc | 2 +- test/util/multiprocess_util.h | 2 +- test/util/posix_error.cc | 2 +- test/util/posix_error.h | 2 +- test/util/posix_error_test.cc | 2 +- test/util/proc_util.cc | 2 +- test/util/proc_util.h | 2 +- test/util/proc_util_test.cc | 2 +- test/util/rlimit_util.cc | 2 +- test/util/rlimit_util.h | 2 +- test/util/save_util.cc | 2 +- test/util/save_util.h | 2 +- test/util/signal_util.cc | 2 +- test/util/signal_util.h | 2 +- test/util/temp_path.cc | 2 +- test/util/temp_path.h | 2 +- test/util/test_main.cc | 2 +- test/util/test_util.cc | 2 +- test/util/test_util.h | 2 +- test/util/test_util_test.cc | 2 +- test/util/thread_util.h | 2 +- test/util/timer_util.cc | 2 +- test/util/timer_util.h | 2 +- third_party/gvsync/atomicptr_unsafe.go | 2 +- third_party/gvsync/atomicptrtest/atomicptr_test.go | 2 +- third_party/gvsync/downgradable_rwmutex_test.go | 2 +- third_party/gvsync/downgradable_rwmutex_unsafe.go | 2 +- third_party/gvsync/gvsync.go | 2 +- third_party/gvsync/memmove_unsafe.go | 2 +- third_party/gvsync/norace_unsafe.go | 2 +- third_party/gvsync/race_unsafe.go | 2 +- third_party/gvsync/seqatomic_unsafe.go | 2 +- third_party/gvsync/seqatomictest/seqatomic_test.go | 2 +- third_party/gvsync/seqcount.go | 2 +- third_party/gvsync/seqcount_test.go | 2 +- tools/go_generics/generics.go | 2 +- tools/go_generics/generics_tests/all_stmts/input.go | 2 +- tools/go_generics/generics_tests/all_stmts/output/output.go | 2 +- tools/go_generics/generics_tests/all_types/input.go | 2 +- tools/go_generics/generics_tests/all_types/lib/lib.go | 2 +- tools/go_generics/generics_tests/all_types/output/output.go | 2 +- tools/go_generics/generics_tests/consts/input.go | 2 +- tools/go_generics/generics_tests/consts/output/output.go | 2 +- tools/go_generics/generics_tests/imports/input.go | 2 +- tools/go_generics/generics_tests/imports/output/output.go | 2 +- tools/go_generics/generics_tests/remove_typedef/input.go | 2 +- tools/go_generics/generics_tests/remove_typedef/output/output.go | 2 +- tools/go_generics/generics_tests/simple/input.go | 2 +- tools/go_generics/generics_tests/simple/output/output.go | 2 +- tools/go_generics/globals/globals_visitor.go | 2 +- tools/go_generics/globals/scope.go | 2 +- tools/go_generics/go_generics_unittest.sh | 2 +- tools/go_generics/go_merge/main.go | 2 +- tools/go_generics/imports.go | 2 +- tools/go_generics/remove.go | 2 +- tools/go_generics/rules_tests/template.go | 2 +- tools/go_generics/rules_tests/template_test.go | 2 +- tools/go_stateify/main.go | 2 +- tools/tag_release.sh | 2 +- tools/workspace_status.sh | 2 +- vdso/barrier.h | 2 +- vdso/check_vdso.py | 2 +- vdso/compiler.h | 2 +- vdso/cycle_clock.h | 2 +- vdso/seqlock.h | 2 +- vdso/syscalls.h | 2 +- vdso/vdso.cc | 2 +- vdso/vdso_time.cc | 2 +- vdso/vdso_time.h | 2 +- 1235 files changed, 1242 insertions(+), 1234 deletions(-) create mode 100644 AUTHORS (limited to 'runsc/specutils') diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..01ba46567 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,8 @@ +# This is the list of gVisor authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +# +# Please send a patch if you would like to be included in this list. +Google LLC diff --git a/kokoro/run_build.sh b/kokoro/run_build.sh index 89e24b037..63fffda48 100755 --- a/kokoro/run_build.sh +++ b/kokoro/run_build.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/kokoro/run_tests.sh b/kokoro/run_tests.sh index 8a3ce7402..08f678e39 100755 --- a/kokoro/run_tests.sh +++ b/kokoro/run_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/pkg/abi/abi.go b/pkg/abi/abi.go index 7770f0405..d56c481c9 100644 --- a/pkg/abi/abi.go +++ b/pkg/abi/abi.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/abi_linux.go b/pkg/abi/abi_linux.go index 9d9f361a4..3059479bd 100644 --- a/pkg/abi/abi_linux.go +++ b/pkg/abi/abi_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/flag.go b/pkg/abi/flag.go index b48757da8..dcdd66d4e 100644 --- a/pkg/abi/flag.go +++ b/pkg/abi/flag.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/aio.go b/pkg/abi/linux/aio.go index 1b7ca714a..3c6e0079d 100644 --- a/pkg/abi/linux/aio.go +++ b/pkg/abi/linux/aio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/ashmem.go b/pkg/abi/linux/ashmem.go index ced1e44d4..2a722abe0 100644 --- a/pkg/abi/linux/ashmem.go +++ b/pkg/abi/linux/ashmem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/audit.go b/pkg/abi/linux/audit.go index b39ba4515..6cca69af9 100644 --- a/pkg/abi/linux/audit.go +++ b/pkg/abi/linux/audit.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/binder.go b/pkg/abi/linux/binder.go index 522dc6f53..63b08324a 100644 --- a/pkg/abi/linux/binder.go +++ b/pkg/abi/linux/binder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/bpf.go b/pkg/abi/linux/bpf.go index d9cd09948..aa3d3ce70 100644 --- a/pkg/abi/linux/bpf.go +++ b/pkg/abi/linux/bpf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/capability.go b/pkg/abi/linux/capability.go index 7d96f013e..c120cac64 100644 --- a/pkg/abi/linux/capability.go +++ b/pkg/abi/linux/capability.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/dev.go b/pkg/abi/linux/dev.go index 5b1199aac..421e11256 100644 --- a/pkg/abi/linux/dev.go +++ b/pkg/abi/linux/dev.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/elf.go b/pkg/abi/linux/elf.go index 928067c04..fb1c679d2 100644 --- a/pkg/abi/linux/elf.go +++ b/pkg/abi/linux/elf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/errors.go b/pkg/abi/linux/errors.go index e5f6f3f07..93f85a864 100644 --- a/pkg/abi/linux/errors.go +++ b/pkg/abi/linux/errors.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/eventfd.go b/pkg/abi/linux/eventfd.go index 5614f5cf1..9c479fc8f 100644 --- a/pkg/abi/linux/eventfd.go +++ b/pkg/abi/linux/eventfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/exec.go b/pkg/abi/linux/exec.go index a07c29243..579d46c41 100644 --- a/pkg/abi/linux/exec.go +++ b/pkg/abi/linux/exec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/fcntl.go b/pkg/abi/linux/fcntl.go index c8558933a..cc8f2702d 100644 --- a/pkg/abi/linux/fcntl.go +++ b/pkg/abi/linux/fcntl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/file.go b/pkg/abi/linux/file.go index 46b10ca97..753fec3ed 100644 --- a/pkg/abi/linux/file.go +++ b/pkg/abi/linux/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/fs.go b/pkg/abi/linux/fs.go index a9f2ba132..c82ab9b5b 100644 --- a/pkg/abi/linux/fs.go +++ b/pkg/abi/linux/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/futex.go b/pkg/abi/linux/futex.go index afdf4123b..08bfde3b5 100644 --- a/pkg/abi/linux/futex.go +++ b/pkg/abi/linux/futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/inotify.go b/pkg/abi/linux/inotify.go index 79c5d3593..2d08194ba 100644 --- a/pkg/abi/linux/inotify.go +++ b/pkg/abi/linux/inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/ioctl.go b/pkg/abi/linux/ioctl.go index 191b26e4d..04bb767dc 100644 --- a/pkg/abi/linux/ioctl.go +++ b/pkg/abi/linux/ioctl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/ip.go b/pkg/abi/linux/ip.go index 77ac1062c..31e56ffa6 100644 --- a/pkg/abi/linux/ip.go +++ b/pkg/abi/linux/ip.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/ipc.go b/pkg/abi/linux/ipc.go index 10681768b..2ef8d6cbb 100644 --- a/pkg/abi/linux/ipc.go +++ b/pkg/abi/linux/ipc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/limits.go b/pkg/abi/linux/limits.go index e0aa5b31d..c74dfcd53 100644 --- a/pkg/abi/linux/limits.go +++ b/pkg/abi/linux/limits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/linux.go b/pkg/abi/linux/linux.go index d365f693d..8a8f831cd 100644 --- a/pkg/abi/linux/linux.go +++ b/pkg/abi/linux/linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/mm.go b/pkg/abi/linux/mm.go index eda8d9788..0b02f938a 100644 --- a/pkg/abi/linux/mm.go +++ b/pkg/abi/linux/mm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/netdevice.go b/pkg/abi/linux/netdevice.go index e3b6b1e40..aef1acf75 100644 --- a/pkg/abi/linux/netdevice.go +++ b/pkg/abi/linux/netdevice.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/netlink.go b/pkg/abi/linux/netlink.go index 25c5e17fd..5e718c363 100644 --- a/pkg/abi/linux/netlink.go +++ b/pkg/abi/linux/netlink.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/netlink_route.go b/pkg/abi/linux/netlink_route.go index 4200b6506..630dc339a 100644 --- a/pkg/abi/linux/netlink_route.go +++ b/pkg/abi/linux/netlink_route.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/poll.go b/pkg/abi/linux/poll.go index 9f0b15d1c..c04d26e4c 100644 --- a/pkg/abi/linux/poll.go +++ b/pkg/abi/linux/poll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/prctl.go b/pkg/abi/linux/prctl.go index db3206f36..dae2de290 100644 --- a/pkg/abi/linux/prctl.go +++ b/pkg/abi/linux/prctl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/ptrace.go b/pkg/abi/linux/ptrace.go index 7db4f5464..23e605ab2 100644 --- a/pkg/abi/linux/ptrace.go +++ b/pkg/abi/linux/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/rusage.go b/pkg/abi/linux/rusage.go index 7fea4b589..d8302dc85 100644 --- a/pkg/abi/linux/rusage.go +++ b/pkg/abi/linux/rusage.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/sched.go b/pkg/abi/linux/sched.go index ef96a3801..193d9a242 100644 --- a/pkg/abi/linux/sched.go +++ b/pkg/abi/linux/sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/seccomp.go b/pkg/abi/linux/seccomp.go index 8673a27bf..4eeb5cd7a 100644 --- a/pkg/abi/linux/seccomp.go +++ b/pkg/abi/linux/seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/sem.go b/pkg/abi/linux/sem.go index b80c93daf..de422c519 100644 --- a/pkg/abi/linux/sem.go +++ b/pkg/abi/linux/sem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/shm.go b/pkg/abi/linux/shm.go index 82a80e609..e45aadb10 100644 --- a/pkg/abi/linux/shm.go +++ b/pkg/abi/linux/shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/signal.go b/pkg/abi/linux/signal.go index 395f9f31e..9cbd77dda 100644 --- a/pkg/abi/linux/signal.go +++ b/pkg/abi/linux/signal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/socket.go b/pkg/abi/linux/socket.go index 6fa4e7c3e..417840731 100644 --- a/pkg/abi/linux/socket.go +++ b/pkg/abi/linux/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/tcp.go b/pkg/abi/linux/tcp.go index 67908deb9..174d470e2 100644 --- a/pkg/abi/linux/tcp.go +++ b/pkg/abi/linux/tcp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/time.go b/pkg/abi/linux/time.go index bbd21e726..fa9ee27e1 100644 --- a/pkg/abi/linux/time.go +++ b/pkg/abi/linux/time.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/timer.go b/pkg/abi/linux/timer.go index a6f420bdb..e32d09e10 100644 --- a/pkg/abi/linux/timer.go +++ b/pkg/abi/linux/timer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/tty.go b/pkg/abi/linux/tty.go index bff882d89..8ac02aee8 100644 --- a/pkg/abi/linux/tty.go +++ b/pkg/abi/linux/tty.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/uio.go b/pkg/abi/linux/uio.go index 7e00d9959..1fd1e9802 100644 --- a/pkg/abi/linux/uio.go +++ b/pkg/abi/linux/uio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/abi/linux/utsname.go b/pkg/abi/linux/utsname.go index f80ed7d4a..60f220a67 100644 --- a/pkg/abi/linux/utsname.go +++ b/pkg/abi/linux/utsname.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/amutex/amutex.go b/pkg/amutex/amutex.go index 26b674435..85e819304 100644 --- a/pkg/amutex/amutex.go +++ b/pkg/amutex/amutex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/amutex/amutex_test.go b/pkg/amutex/amutex_test.go index 104e0dab1..6a0af006e 100644 --- a/pkg/amutex/amutex_test.go +++ b/pkg/amutex/amutex_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/atomicbitops/atomic_bitops.go b/pkg/atomicbitops/atomic_bitops.go index 9a57f9599..63aa2b7f1 100644 --- a/pkg/atomicbitops/atomic_bitops.go +++ b/pkg/atomicbitops/atomic_bitops.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/atomicbitops/atomic_bitops_amd64.s b/pkg/atomicbitops/atomic_bitops_amd64.s index b37e3aad3..db0972001 100644 --- a/pkg/atomicbitops/atomic_bitops_amd64.s +++ b/pkg/atomicbitops/atomic_bitops_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/atomicbitops/atomic_bitops_common.go b/pkg/atomicbitops/atomic_bitops_common.go index b03242baa..b2a943dcb 100644 --- a/pkg/atomicbitops/atomic_bitops_common.go +++ b/pkg/atomicbitops/atomic_bitops_common.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/atomicbitops/atomic_bitops_test.go b/pkg/atomicbitops/atomic_bitops_test.go index ee6207cb3..965e9be79 100644 --- a/pkg/atomicbitops/atomic_bitops_test.go +++ b/pkg/atomicbitops/atomic_bitops_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/binary/binary.go b/pkg/binary/binary.go index 02f7e9fb8..631785f7b 100644 --- a/pkg/binary/binary.go +++ b/pkg/binary/binary.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/binary/binary_test.go b/pkg/binary/binary_test.go index 200961c70..4d609a438 100644 --- a/pkg/binary/binary_test.go +++ b/pkg/binary/binary_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bits/bits.go b/pkg/bits/bits.go index eb3c80f49..a26433ad6 100644 --- a/pkg/bits/bits.go +++ b/pkg/bits/bits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bits/bits_template.go b/pkg/bits/bits_template.go index 8c578cca2..93a435b80 100644 --- a/pkg/bits/bits_template.go +++ b/pkg/bits/bits_template.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bits/uint64_arch_amd64.go b/pkg/bits/uint64_arch_amd64.go index 1fef89394..faccaa61a 100644 --- a/pkg/bits/uint64_arch_amd64.go +++ b/pkg/bits/uint64_arch_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bits/uint64_arch_amd64_asm.s b/pkg/bits/uint64_arch_amd64_asm.s index 8c7322f0f..8ff364181 100644 --- a/pkg/bits/uint64_arch_amd64_asm.s +++ b/pkg/bits/uint64_arch_amd64_asm.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bits/uint64_arch_generic.go b/pkg/bits/uint64_arch_generic.go index cfb47400b..7dd2d1480 100644 --- a/pkg/bits/uint64_arch_generic.go +++ b/pkg/bits/uint64_arch_generic.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bits/uint64_test.go b/pkg/bits/uint64_test.go index d6dbaf602..1b018d808 100644 --- a/pkg/bits/uint64_test.go +++ b/pkg/bits/uint64_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/bpf.go b/pkg/bpf/bpf.go index 98d44d911..eb546f48f 100644 --- a/pkg/bpf/bpf.go +++ b/pkg/bpf/bpf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/decoder.go b/pkg/bpf/decoder.go index ae6b8839a..45c192215 100644 --- a/pkg/bpf/decoder.go +++ b/pkg/bpf/decoder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/decoder_test.go b/pkg/bpf/decoder_test.go index f093e1e41..8c4bdad21 100644 --- a/pkg/bpf/decoder_test.go +++ b/pkg/bpf/decoder_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/input_bytes.go b/pkg/bpf/input_bytes.go index 745c0749b..86b216cfc 100644 --- a/pkg/bpf/input_bytes.go +++ b/pkg/bpf/input_bytes.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/interpreter.go b/pkg/bpf/interpreter.go index 86c7add4d..86de523a2 100644 --- a/pkg/bpf/interpreter.go +++ b/pkg/bpf/interpreter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/interpreter_test.go b/pkg/bpf/interpreter_test.go index c46a43991..67b00ffe3 100644 --- a/pkg/bpf/interpreter_test.go +++ b/pkg/bpf/interpreter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/program_builder.go b/pkg/bpf/program_builder.go index b4ce228e1..fc9d27203 100644 --- a/pkg/bpf/program_builder.go +++ b/pkg/bpf/program_builder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/bpf/program_builder_test.go b/pkg/bpf/program_builder_test.go index 0e0b79d88..5b2ad67de 100644 --- a/pkg/bpf/program_builder_test.go +++ b/pkg/bpf/program_builder_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/compressio/compressio.go b/pkg/compressio/compressio.go index 4daaa82b6..8c14ccbfa 100644 --- a/pkg/compressio/compressio.go +++ b/pkg/compressio/compressio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/compressio/compressio_test.go b/pkg/compressio/compressio_test.go index 1bbabee79..86dc47e44 100644 --- a/pkg/compressio/compressio_test.go +++ b/pkg/compressio/compressio_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/control/client/client.go b/pkg/control/client/client.go index 0d0c9f148..3fec27846 100644 --- a/pkg/control/client/client.go +++ b/pkg/control/client/client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/control/server/server.go b/pkg/control/server/server.go index c46b5d70b..1a15da1a8 100644 --- a/pkg/control/server/server.go +++ b/pkg/control/server/server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/cpuid/cpu_amd64.s b/pkg/cpuid/cpu_amd64.s index 905c1d12e..ac80d3c8a 100644 --- a/pkg/cpuid/cpu_amd64.s +++ b/pkg/cpuid/cpu_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/cpuid/cpuid.go b/pkg/cpuid/cpuid.go index 61441150e..3eb2bcd2b 100644 --- a/pkg/cpuid/cpuid.go +++ b/pkg/cpuid/cpuid.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/cpuid/cpuid_parse_test.go b/pkg/cpuid/cpuid_parse_test.go index e8f87a10e..dd9969db4 100644 --- a/pkg/cpuid/cpuid_parse_test.go +++ b/pkg/cpuid/cpuid_parse_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/cpuid/cpuid_test.go b/pkg/cpuid/cpuid_test.go index 64ade1cbe..6ae14d2da 100644 --- a/pkg/cpuid/cpuid_test.go +++ b/pkg/cpuid/cpuid_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/dhcp/client.go b/pkg/dhcp/client.go index 2ba79be32..b7cde3819 100644 --- a/pkg/dhcp/client.go +++ b/pkg/dhcp/client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/dhcp/dhcp.go b/pkg/dhcp/dhcp.go index 6945bcd35..f96ffd891 100644 --- a/pkg/dhcp/dhcp.go +++ b/pkg/dhcp/dhcp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/dhcp/dhcp_string.go b/pkg/dhcp/dhcp_string.go index 8533895bd..29ce98593 100644 --- a/pkg/dhcp/dhcp_string.go +++ b/pkg/dhcp/dhcp_string.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/dhcp/dhcp_test.go b/pkg/dhcp/dhcp_test.go index e1d8ef603..751626bb0 100644 --- a/pkg/dhcp/dhcp_test.go +++ b/pkg/dhcp/dhcp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/dhcp/server.go b/pkg/dhcp/server.go index 9549ff705..6a1972860 100644 --- a/pkg/dhcp/server.go +++ b/pkg/dhcp/server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/eventchannel/event.go b/pkg/eventchannel/event.go index 41a7b5ed3..4c8ae573b 100644 --- a/pkg/eventchannel/event.go +++ b/pkg/eventchannel/event.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/eventchannel/event.proto b/pkg/eventchannel/event.proto index c1679c7e7..34468f072 100644 --- a/pkg/eventchannel/event.proto +++ b/pkg/eventchannel/event.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/fd/fd.go b/pkg/fd/fd.go index d40758c22..2785243a2 100644 --- a/pkg/fd/fd.go +++ b/pkg/fd/fd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/fd/fd_test.go b/pkg/fd/fd_test.go index 42bb3ef6c..5fb0ad47d 100644 --- a/pkg/fd/fd_test.go +++ b/pkg/fd/fd_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/fdnotifier/fdnotifier.go b/pkg/fdnotifier/fdnotifier.go index aa4906ca0..f0b028b0b 100644 --- a/pkg/fdnotifier/fdnotifier.go +++ b/pkg/fdnotifier/fdnotifier.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/fdnotifier/poll_unsafe.go b/pkg/fdnotifier/poll_unsafe.go index 05be9aeb5..bc5e0ac44 100644 --- a/pkg/fdnotifier/poll_unsafe.go +++ b/pkg/fdnotifier/poll_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/gate/gate.go b/pkg/gate/gate.go index 48122bf5a..bda6aae09 100644 --- a/pkg/gate/gate.go +++ b/pkg/gate/gate.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/gate/gate_test.go b/pkg/gate/gate_test.go index 95620fa8e..7467e7d07 100644 --- a/pkg/gate/gate_test.go +++ b/pkg/gate/gate_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/ilist/list.go b/pkg/ilist/list.go index 51c9b6df3..019caadca 100644 --- a/pkg/ilist/list.go +++ b/pkg/ilist/list.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/ilist/list_test.go b/pkg/ilist/list_test.go index f37946dc2..3f9abfb56 100644 --- a/pkg/ilist/list_test.go +++ b/pkg/ilist/list_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/linewriter/linewriter.go b/pkg/linewriter/linewriter.go index 5fbd4e779..cd6e4e2ce 100644 --- a/pkg/linewriter/linewriter.go +++ b/pkg/linewriter/linewriter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/linewriter/linewriter_test.go b/pkg/linewriter/linewriter_test.go index 9140ee6af..96dc7e6e0 100644 --- a/pkg/linewriter/linewriter_test.go +++ b/pkg/linewriter/linewriter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/log/glog.go b/pkg/log/glog.go index 24d5390d7..5732785b4 100644 --- a/pkg/log/glog.go +++ b/pkg/log/glog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/log/glog_unsafe.go b/pkg/log/glog_unsafe.go index bb06aa7d3..ea17ae349 100644 --- a/pkg/log/glog_unsafe.go +++ b/pkg/log/glog_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/log/json.go b/pkg/log/json.go index 96bd13d87..a278c8fc8 100644 --- a/pkg/log/json.go +++ b/pkg/log/json.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/log/json_k8s.go b/pkg/log/json_k8s.go index 9c2f8d2b7..c2c019915 100644 --- a/pkg/log/json_k8s.go +++ b/pkg/log/json_k8s.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/log/json_test.go b/pkg/log/json_test.go index b8c7a795e..f25224fe1 100644 --- a/pkg/log/json_test.go +++ b/pkg/log/json_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/log/log.go b/pkg/log/log.go index b8d456aae..7d563241e 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go index a59d457dd..0634e7c1f 100644 --- a/pkg/log/log_test.go +++ b/pkg/log/log_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/metric/metric.go b/pkg/metric/metric.go index e5eb95f89..803709cc4 100644 --- a/pkg/metric/metric.go +++ b/pkg/metric/metric.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/metric/metric.proto b/pkg/metric/metric.proto index 917fda1ac..a2c2bd1ba 100644 --- a/pkg/metric/metric.proto +++ b/pkg/metric/metric.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/metric/metric_test.go b/pkg/metric/metric_test.go index 40034a589..b8b124c83 100644 --- a/pkg/metric/metric_test.go +++ b/pkg/metric/metric_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/buffer.go b/pkg/p9/buffer.go index b7bb14ef9..4c8c6555d 100644 --- a/pkg/p9/buffer.go +++ b/pkg/p9/buffer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/buffer_test.go b/pkg/p9/buffer_test.go index 18d55e5c0..a9c75f86b 100644 --- a/pkg/p9/buffer_test.go +++ b/pkg/p9/buffer_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/client.go b/pkg/p9/client.go index 67887874a..2f9c716d0 100644 --- a/pkg/p9/client.go +++ b/pkg/p9/client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/client_file.go b/pkg/p9/client_file.go index 992d1daf7..63c65129a 100644 --- a/pkg/p9/client_file.go +++ b/pkg/p9/client_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/client_test.go b/pkg/p9/client_test.go index f7145452d..fc49729d8 100644 --- a/pkg/p9/client_test.go +++ b/pkg/p9/client_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/file.go b/pkg/p9/file.go index 55ceb52e1..a52a0f3e7 100644 --- a/pkg/p9/file.go +++ b/pkg/p9/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/handlers.go b/pkg/p9/handlers.go index c1d1ac1e8..6da2ce4e3 100644 --- a/pkg/p9/handlers.go +++ b/pkg/p9/handlers.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/local_server/local_server.go b/pkg/p9/local_server/local_server.go index 69b90c6cd..f4077a9d4 100644 --- a/pkg/p9/local_server/local_server.go +++ b/pkg/p9/local_server/local_server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go index 97decd3cc..833defbd6 100644 --- a/pkg/p9/messages.go +++ b/pkg/p9/messages.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/messages_test.go b/pkg/p9/messages_test.go index 68395a396..10a0587cf 100644 --- a/pkg/p9/messages_test.go +++ b/pkg/p9/messages_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/p9.go b/pkg/p9/p9.go index 4ea9f2f9a..78c7d3f86 100644 --- a/pkg/p9/p9.go +++ b/pkg/p9/p9.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/p9_test.go b/pkg/p9/p9_test.go index 02498346c..8dda6cc64 100644 --- a/pkg/p9/p9_test.go +++ b/pkg/p9/p9_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/p9test/client_test.go b/pkg/p9/p9test/client_test.go index 242d81b95..e00dd03ab 100644 --- a/pkg/p9/p9test/client_test.go +++ b/pkg/p9/p9test/client_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/p9test/p9test.go b/pkg/p9/p9test/p9test.go index f9bacbf84..1c8eff200 100644 --- a/pkg/p9/p9test/p9test.go +++ b/pkg/p9/p9test/p9test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/path_tree.go b/pkg/p9/path_tree.go index 60b20578e..f37ad4ab2 100644 --- a/pkg/p9/path_tree.go +++ b/pkg/p9/path_tree.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/pool.go b/pkg/p9/pool.go index 34ed898e8..52de889e1 100644 --- a/pkg/p9/pool.go +++ b/pkg/p9/pool.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/pool_test.go b/pkg/p9/pool_test.go index 71052d8c4..e4746b8da 100644 --- a/pkg/p9/pool_test.go +++ b/pkg/p9/pool_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/server.go b/pkg/p9/server.go index 3ef151595..b2a86d8fa 100644 --- a/pkg/p9/server.go +++ b/pkg/p9/server.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/transport.go b/pkg/p9/transport.go index bafb377de..ef59077ff 100644 --- a/pkg/p9/transport.go +++ b/pkg/p9/transport.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/transport_test.go b/pkg/p9/transport_test.go index b7b7825bd..c833d1c9c 100644 --- a/pkg/p9/transport_test.go +++ b/pkg/p9/transport_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/version.go b/pkg/p9/version.go index ceb6fabbf..a36a499a1 100644 --- a/pkg/p9/version.go +++ b/pkg/p9/version.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/p9/version_test.go b/pkg/p9/version_test.go index c053614c9..291e8580e 100644 --- a/pkg/p9/version_test.go +++ b/pkg/p9/version_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/rand/rand.go b/pkg/rand/rand.go index 593a14380..a2714784d 100644 --- a/pkg/rand/rand.go +++ b/pkg/rand/rand.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/rand/rand_linux.go b/pkg/rand/rand_linux.go index 7ebe8f3b0..2b92db3e6 100644 --- a/pkg/rand/rand_linux.go +++ b/pkg/rand/rand_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/refs/refcounter.go b/pkg/refs/refcounter.go index 8f08c74c7..20f515391 100644 --- a/pkg/refs/refcounter.go +++ b/pkg/refs/refcounter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/refs/refcounter_state.go b/pkg/refs/refcounter_state.go index 136f06fbf..7c99fd2b5 100644 --- a/pkg/refs/refcounter_state.go +++ b/pkg/refs/refcounter_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/refs/refcounter_test.go b/pkg/refs/refcounter_test.go index abaa87453..ffd3d3f07 100644 --- a/pkg/refs/refcounter_test.go +++ b/pkg/refs/refcounter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go index e113f3574..50c9409e4 100644 --- a/pkg/seccomp/seccomp.go +++ b/pkg/seccomp/seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/seccomp/seccomp_rules.go b/pkg/seccomp/seccomp_rules.go index a9278c64b..29eec8db1 100644 --- a/pkg/seccomp/seccomp_rules.go +++ b/pkg/seccomp/seccomp_rules.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/seccomp/seccomp_test.go b/pkg/seccomp/seccomp_test.go index 11ed90eb4..47ecac6f7 100644 --- a/pkg/seccomp/seccomp_test.go +++ b/pkg/seccomp/seccomp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/seccomp/seccomp_test_victim.go b/pkg/seccomp/seccomp_test_victim.go index dd5ed0041..afc2f755f 100644 --- a/pkg/seccomp/seccomp_test_victim.go +++ b/pkg/seccomp/seccomp_test_victim.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/seccomp/seccomp_unsafe.go b/pkg/seccomp/seccomp_unsafe.go index a31c6471d..ccd40d9db 100644 --- a/pkg/seccomp/seccomp_unsafe.go +++ b/pkg/seccomp/seccomp_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/secio/full_reader.go b/pkg/secio/full_reader.go index 90b1772a7..aed2564bd 100644 --- a/pkg/secio/full_reader.go +++ b/pkg/secio/full_reader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/secio/secio.go b/pkg/secio/secio.go index e5f74a497..b43226035 100644 --- a/pkg/secio/secio.go +++ b/pkg/secio/secio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/secio/secio_test.go b/pkg/secio/secio_test.go index 8304c4f74..d1d905187 100644 --- a/pkg/secio/secio_test.go +++ b/pkg/secio/secio_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/segment/range.go b/pkg/segment/range.go index 057bcd7ff..4d4aeffef 100644 --- a/pkg/segment/range.go +++ b/pkg/segment/range.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/segment/set.go b/pkg/segment/set.go index 74a916ea3..982eb3fdd 100644 --- a/pkg/segment/set.go +++ b/pkg/segment/set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/segment/set_state.go b/pkg/segment/set_state.go index b86e1b75f..76de92591 100644 --- a/pkg/segment/set_state.go +++ b/pkg/segment/set_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/segment/test/segment_test.go b/pkg/segment/test/segment_test.go index 0825105db..f19a005f3 100644 --- a/pkg/segment/test/segment_test.go +++ b/pkg/segment/test/segment_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/segment/test/set_functions.go b/pkg/segment/test/set_functions.go index 41f649011..bcddb39bb 100644 --- a/pkg/segment/test/set_functions.go +++ b/pkg/segment/test/set_functions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/aligned.go b/pkg/sentry/arch/aligned.go index c88c034f6..df01a903d 100644 --- a/pkg/sentry/arch/aligned.go +++ b/pkg/sentry/arch/aligned.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/arch.go b/pkg/sentry/arch/arch.go index 16d8eb2b2..53f0c9018 100644 --- a/pkg/sentry/arch/arch.go +++ b/pkg/sentry/arch/arch.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/arch_amd64.go b/pkg/sentry/arch/arch_amd64.go index 7ec2f2c84..135c2ee1f 100644 --- a/pkg/sentry/arch/arch_amd64.go +++ b/pkg/sentry/arch/arch_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/arch_amd64.s b/pkg/sentry/arch/arch_amd64.s index fa9857df7..bd61402cf 100644 --- a/pkg/sentry/arch/arch_amd64.s +++ b/pkg/sentry/arch/arch_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/arch_state_x86.go b/pkg/sentry/arch/arch_state_x86.go index 01949049d..bb52d8db0 100644 --- a/pkg/sentry/arch/arch_state_x86.go +++ b/pkg/sentry/arch/arch_state_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/arch_x86.go b/pkg/sentry/arch/arch_x86.go index 4305fe2cb..4d167ce98 100644 --- a/pkg/sentry/arch/arch_x86.go +++ b/pkg/sentry/arch/arch_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/auxv.go b/pkg/sentry/arch/auxv.go index 5df65a691..80c923103 100644 --- a/pkg/sentry/arch/auxv.go +++ b/pkg/sentry/arch/auxv.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/registers.proto b/pkg/sentry/arch/registers.proto index f4c2f7043..9dc83e241 100644 --- a/pkg/sentry/arch/registers.proto +++ b/pkg/sentry/arch/registers.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/signal_act.go b/pkg/sentry/arch/signal_act.go index ad098c746..f9ca2e74e 100644 --- a/pkg/sentry/arch/signal_act.go +++ b/pkg/sentry/arch/signal_act.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/signal_amd64.go b/pkg/sentry/arch/signal_amd64.go index 7f76eba27..aa030fd70 100644 --- a/pkg/sentry/arch/signal_amd64.go +++ b/pkg/sentry/arch/signal_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/signal_info.go b/pkg/sentry/arch/signal_info.go index fa0ecbec5..f93ee8b46 100644 --- a/pkg/sentry/arch/signal_info.go +++ b/pkg/sentry/arch/signal_info.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/signal_stack.go b/pkg/sentry/arch/signal_stack.go index c02ae3b7c..a442f9fdc 100644 --- a/pkg/sentry/arch/signal_stack.go +++ b/pkg/sentry/arch/signal_stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/stack.go b/pkg/sentry/arch/stack.go index 2e33ccdf5..7e6324e82 100644 --- a/pkg/sentry/arch/stack.go +++ b/pkg/sentry/arch/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/arch/syscalls_amd64.go b/pkg/sentry/arch/syscalls_amd64.go index 47c31d4b9..8b4f23007 100644 --- a/pkg/sentry/arch/syscalls_amd64.go +++ b/pkg/sentry/arch/syscalls_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/context/context.go b/pkg/sentry/context/context.go index eefc3e1b4..d70f3a5c3 100644 --- a/pkg/sentry/context/context.go +++ b/pkg/sentry/context/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/context/contexttest/contexttest.go b/pkg/sentry/context/contexttest/contexttest.go index a29087775..a42038711 100644 --- a/pkg/sentry/context/contexttest/contexttest.go +++ b/pkg/sentry/context/contexttest/contexttest.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/control/control.go b/pkg/sentry/control/control.go index 32d30b6ea..6060b9b4f 100644 --- a/pkg/sentry/control/control.go +++ b/pkg/sentry/control/control.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/control/pprof.go b/pkg/sentry/control/pprof.go index 1af092af3..94ed149f2 100644 --- a/pkg/sentry/control/pprof.go +++ b/pkg/sentry/control/pprof.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go index aca2267a7..f7f02a3e1 100644 --- a/pkg/sentry/control/proc.go +++ b/pkg/sentry/control/proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/control/proc_test.go b/pkg/sentry/control/proc_test.go index 5d52cd829..b7895d03c 100644 --- a/pkg/sentry/control/proc_test.go +++ b/pkg/sentry/control/proc_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/control/state.go b/pkg/sentry/control/state.go index b6bbf69fa..11efcaba1 100644 --- a/pkg/sentry/control/state.go +++ b/pkg/sentry/control/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/device/device.go b/pkg/sentry/device/device.go index ae4fa1d93..458d03b30 100644 --- a/pkg/sentry/device/device.go +++ b/pkg/sentry/device/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/device/device_test.go b/pkg/sentry/device/device_test.go index 5d8805c2f..e3f51ce4f 100644 --- a/pkg/sentry/device/device_test.go +++ b/pkg/sentry/device/device_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/anon/anon.go b/pkg/sentry/fs/anon/anon.go index a5e8c4f0d..a6ea8b9e7 100644 --- a/pkg/sentry/fs/anon/anon.go +++ b/pkg/sentry/fs/anon/anon.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/anon/device.go b/pkg/sentry/fs/anon/device.go index 2d1249299..5927bd11e 100644 --- a/pkg/sentry/fs/anon/device.go +++ b/pkg/sentry/fs/anon/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ashmem/area.go b/pkg/sentry/fs/ashmem/area.go index 1f61c5711..b53746519 100644 --- a/pkg/sentry/fs/ashmem/area.go +++ b/pkg/sentry/fs/ashmem/area.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ashmem/device.go b/pkg/sentry/fs/ashmem/device.go index 5369d1b0d..5e005bc2e 100644 --- a/pkg/sentry/fs/ashmem/device.go +++ b/pkg/sentry/fs/ashmem/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ashmem/pin_board.go b/pkg/sentry/fs/ashmem/pin_board.go index 7c997f533..bdf23b371 100644 --- a/pkg/sentry/fs/ashmem/pin_board.go +++ b/pkg/sentry/fs/ashmem/pin_board.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ashmem/pin_board_test.go b/pkg/sentry/fs/ashmem/pin_board_test.go index 736e628dc..24f5d86d6 100644 --- a/pkg/sentry/fs/ashmem/pin_board_test.go +++ b/pkg/sentry/fs/ashmem/pin_board_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/attr.go b/pkg/sentry/fs/attr.go index 3523b068a..591e35e6a 100644 --- a/pkg/sentry/fs/attr.go +++ b/pkg/sentry/fs/attr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/binder/binder.go b/pkg/sentry/fs/binder/binder.go index d9f1559de..acbbd5466 100644 --- a/pkg/sentry/fs/binder/binder.go +++ b/pkg/sentry/fs/binder/binder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/context.go b/pkg/sentry/fs/context.go index 4869428a8..c80ea0175 100644 --- a/pkg/sentry/fs/context.go +++ b/pkg/sentry/fs/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/copy_up.go b/pkg/sentry/fs/copy_up.go index ba69e718d..ee2d3d115 100644 --- a/pkg/sentry/fs/copy_up.go +++ b/pkg/sentry/fs/copy_up.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/copy_up_test.go b/pkg/sentry/fs/copy_up_test.go index 98a0b7638..54810afca 100644 --- a/pkg/sentry/fs/copy_up_test.go +++ b/pkg/sentry/fs/copy_up_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dentry.go b/pkg/sentry/fs/dentry.go index 29fb155a4..fe656cc24 100644 --- a/pkg/sentry/fs/dentry.go +++ b/pkg/sentry/fs/dentry.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dev/dev.go b/pkg/sentry/fs/dev/dev.go index fbc750a71..34ac01173 100644 --- a/pkg/sentry/fs/dev/dev.go +++ b/pkg/sentry/fs/dev/dev.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dev/device.go b/pkg/sentry/fs/dev/device.go index 3cecdf6e2..9f4e41fc9 100644 --- a/pkg/sentry/fs/dev/device.go +++ b/pkg/sentry/fs/dev/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dev/fs.go b/pkg/sentry/fs/dev/fs.go index cf4e7d00f..6096a40f8 100644 --- a/pkg/sentry/fs/dev/fs.go +++ b/pkg/sentry/fs/dev/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dev/full.go b/pkg/sentry/fs/dev/full.go index 82da9aae9..6b11afa44 100644 --- a/pkg/sentry/fs/dev/full.go +++ b/pkg/sentry/fs/dev/full.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dev/null.go b/pkg/sentry/fs/dev/null.go index 5d306d352..069212b6d 100644 --- a/pkg/sentry/fs/dev/null.go +++ b/pkg/sentry/fs/dev/null.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dev/random.go b/pkg/sentry/fs/dev/random.go index ffd5cf6c3..de0f3e5e5 100644 --- a/pkg/sentry/fs/dev/random.go +++ b/pkg/sentry/fs/dev/random.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dirent.go b/pkg/sentry/fs/dirent.go index 54fc11fe1..c0bc261a2 100644 --- a/pkg/sentry/fs/dirent.go +++ b/pkg/sentry/fs/dirent.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dirent_cache.go b/pkg/sentry/fs/dirent_cache.go index d26a06971..71f2d11de 100644 --- a/pkg/sentry/fs/dirent_cache.go +++ b/pkg/sentry/fs/dirent_cache.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dirent_cache_limiter.go b/pkg/sentry/fs/dirent_cache_limiter.go index 024c7b2d5..ebb80bd50 100644 --- a/pkg/sentry/fs/dirent_cache_limiter.go +++ b/pkg/sentry/fs/dirent_cache_limiter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dirent_cache_test.go b/pkg/sentry/fs/dirent_cache_test.go index 93e8d415f..395c879f5 100644 --- a/pkg/sentry/fs/dirent_cache_test.go +++ b/pkg/sentry/fs/dirent_cache_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dirent_refs_test.go b/pkg/sentry/fs/dirent_refs_test.go index 325404e27..db88d850e 100644 --- a/pkg/sentry/fs/dirent_refs_test.go +++ b/pkg/sentry/fs/dirent_refs_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/dirent_state.go b/pkg/sentry/fs/dirent_state.go index 5cf151dab..18652b809 100644 --- a/pkg/sentry/fs/dirent_state.go +++ b/pkg/sentry/fs/dirent_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fdpipe/pipe.go b/pkg/sentry/fs/fdpipe/pipe.go index 98483ab68..95e66ea8d 100644 --- a/pkg/sentry/fs/fdpipe/pipe.go +++ b/pkg/sentry/fs/fdpipe/pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fdpipe/pipe_opener.go b/pkg/sentry/fs/fdpipe/pipe_opener.go index 92ab6ff0e..0cabe2e18 100644 --- a/pkg/sentry/fs/fdpipe/pipe_opener.go +++ b/pkg/sentry/fs/fdpipe/pipe_opener.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fdpipe/pipe_opener_test.go b/pkg/sentry/fs/fdpipe/pipe_opener_test.go index 69516e048..8c8b1b40c 100644 --- a/pkg/sentry/fs/fdpipe/pipe_opener_test.go +++ b/pkg/sentry/fs/fdpipe/pipe_opener_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fdpipe/pipe_state.go b/pkg/sentry/fs/fdpipe/pipe_state.go index 4395666ad..8b347aa11 100644 --- a/pkg/sentry/fs/fdpipe/pipe_state.go +++ b/pkg/sentry/fs/fdpipe/pipe_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fdpipe/pipe_test.go b/pkg/sentry/fs/fdpipe/pipe_test.go index 7e3ee5257..b59a6aa0e 100644 --- a/pkg/sentry/fs/fdpipe/pipe_test.go +++ b/pkg/sentry/fs/fdpipe/pipe_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/file.go b/pkg/sentry/fs/file.go index 5d5026661..62b35dabc 100644 --- a/pkg/sentry/fs/file.go +++ b/pkg/sentry/fs/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/file_operations.go b/pkg/sentry/fs/file_operations.go index e0fa5135f..ab0acb6eb 100644 --- a/pkg/sentry/fs/file_operations.go +++ b/pkg/sentry/fs/file_operations.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/file_overlay.go b/pkg/sentry/fs/file_overlay.go index 6e680f0a4..948ce9c6f 100644 --- a/pkg/sentry/fs/file_overlay.go +++ b/pkg/sentry/fs/file_overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/file_overlay_test.go b/pkg/sentry/fs/file_overlay_test.go index a4ac58763..6a2b8007c 100644 --- a/pkg/sentry/fs/file_overlay_test.go +++ b/pkg/sentry/fs/file_overlay_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/file_state.go b/pkg/sentry/fs/file_state.go index 1c3bae3e8..523182d59 100644 --- a/pkg/sentry/fs/file_state.go +++ b/pkg/sentry/fs/file_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/file_test.go b/pkg/sentry/fs/file_test.go index f3ed9a70b..d867a0257 100644 --- a/pkg/sentry/fs/file_test.go +++ b/pkg/sentry/fs/file_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/filesystems.go b/pkg/sentry/fs/filesystems.go index a6b27c402..acd84dfcc 100644 --- a/pkg/sentry/fs/filesystems.go +++ b/pkg/sentry/fs/filesystems.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/filetest/filetest.go b/pkg/sentry/fs/filetest/filetest.go index 388a1ce36..f6b827800 100644 --- a/pkg/sentry/fs/filetest/filetest.go +++ b/pkg/sentry/fs/filetest/filetest.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/flags.go b/pkg/sentry/fs/flags.go index bf2a20b33..5c8cb773f 100644 --- a/pkg/sentry/fs/flags.go +++ b/pkg/sentry/fs/flags.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fs.go b/pkg/sentry/fs/fs.go index 119689776..632055cce 100644 --- a/pkg/sentry/fs/fs.go +++ b/pkg/sentry/fs/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/dirty_set.go b/pkg/sentry/fs/fsutil/dirty_set.go index 5add16ac4..9cd196d7d 100644 --- a/pkg/sentry/fs/fsutil/dirty_set.go +++ b/pkg/sentry/fs/fsutil/dirty_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/dirty_set_test.go b/pkg/sentry/fs/fsutil/dirty_set_test.go index f5c9d9215..d9c68baa3 100644 --- a/pkg/sentry/fs/fsutil/dirty_set_test.go +++ b/pkg/sentry/fs/fsutil/dirty_set_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/file.go b/pkg/sentry/fs/fsutil/file.go index 42afdd11c..e355d8594 100644 --- a/pkg/sentry/fs/fsutil/file.go +++ b/pkg/sentry/fs/fsutil/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/file_range_set.go b/pkg/sentry/fs/fsutil/file_range_set.go index 32ebf64ff..b5ac6c71c 100644 --- a/pkg/sentry/fs/fsutil/file_range_set.go +++ b/pkg/sentry/fs/fsutil/file_range_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/frame_ref_set.go b/pkg/sentry/fs/fsutil/frame_ref_set.go index b6e783614..6565c28c8 100644 --- a/pkg/sentry/fs/fsutil/frame_ref_set.go +++ b/pkg/sentry/fs/fsutil/frame_ref_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/fsutil.go b/pkg/sentry/fs/fsutil/fsutil.go index 319c4841b..c9587b1d9 100644 --- a/pkg/sentry/fs/fsutil/fsutil.go +++ b/pkg/sentry/fs/fsutil/fsutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/host_file_mapper.go b/pkg/sentry/fs/fsutil/host_file_mapper.go index 9599665f0..2bdfc0db6 100644 --- a/pkg/sentry/fs/fsutil/host_file_mapper.go +++ b/pkg/sentry/fs/fsutil/host_file_mapper.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/host_file_mapper_state.go b/pkg/sentry/fs/fsutil/host_file_mapper_state.go index bbd15b30b..576d2a3df 100644 --- a/pkg/sentry/fs/fsutil/host_file_mapper_state.go +++ b/pkg/sentry/fs/fsutil/host_file_mapper_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go b/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go index 86df76822..7167be263 100644 --- a/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go +++ b/pkg/sentry/fs/fsutil/host_file_mapper_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/host_mappable.go b/pkg/sentry/fs/fsutil/host_mappable.go index 4a182baa1..28686f3b3 100644 --- a/pkg/sentry/fs/fsutil/host_mappable.go +++ b/pkg/sentry/fs/fsutil/host_mappable.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/inode.go b/pkg/sentry/fs/fsutil/inode.go index 468171a9b..b6366d906 100644 --- a/pkg/sentry/fs/fsutil/inode.go +++ b/pkg/sentry/fs/fsutil/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/inode_cached.go b/pkg/sentry/fs/fsutil/inode_cached.go index ba33b9912..919d2534c 100644 --- a/pkg/sentry/fs/fsutil/inode_cached.go +++ b/pkg/sentry/fs/fsutil/inode_cached.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/fsutil/inode_cached_test.go b/pkg/sentry/fs/fsutil/inode_cached_test.go index 2a8a1639c..661ec41f6 100644 --- a/pkg/sentry/fs/fsutil/inode_cached_test.go +++ b/pkg/sentry/fs/fsutil/inode_cached_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/attr.go b/pkg/sentry/fs/gofer/attr.go index 98700d014..c572f3396 100644 --- a/pkg/sentry/fs/gofer/attr.go +++ b/pkg/sentry/fs/gofer/attr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/cache_policy.go b/pkg/sentry/fs/gofer/cache_policy.go index 51c573aef..35cd0c1d6 100644 --- a/pkg/sentry/fs/gofer/cache_policy.go +++ b/pkg/sentry/fs/gofer/cache_policy.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/context_file.go b/pkg/sentry/fs/gofer/context_file.go index 455953237..d512afefc 100644 --- a/pkg/sentry/fs/gofer/context_file.go +++ b/pkg/sentry/fs/gofer/context_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/device.go b/pkg/sentry/fs/gofer/device.go index 52c5acf48..1de6c247c 100644 --- a/pkg/sentry/fs/gofer/device.go +++ b/pkg/sentry/fs/gofer/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/file.go b/pkg/sentry/fs/gofer/file.go index 35caa42cd..bc2be546e 100644 --- a/pkg/sentry/fs/gofer/file.go +++ b/pkg/sentry/fs/gofer/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/file_state.go b/pkg/sentry/fs/gofer/file_state.go index d0c64003c..31264e065 100644 --- a/pkg/sentry/fs/gofer/file_state.go +++ b/pkg/sentry/fs/gofer/file_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/fs.go b/pkg/sentry/fs/gofer/fs.go index adff0abac..6ab89fcc2 100644 --- a/pkg/sentry/fs/gofer/fs.go +++ b/pkg/sentry/fs/gofer/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/gofer_test.go b/pkg/sentry/fs/gofer/gofer_test.go index 36201f017..29d34da7e 100644 --- a/pkg/sentry/fs/gofer/gofer_test.go +++ b/pkg/sentry/fs/gofer/gofer_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/handles.go b/pkg/sentry/fs/gofer/handles.go index 0b33e80c3..c7098cd36 100644 --- a/pkg/sentry/fs/gofer/handles.go +++ b/pkg/sentry/fs/gofer/handles.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/inode.go b/pkg/sentry/fs/gofer/inode.go index 1181a24cc..f6f20844d 100644 --- a/pkg/sentry/fs/gofer/inode.go +++ b/pkg/sentry/fs/gofer/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/inode_state.go b/pkg/sentry/fs/gofer/inode_state.go index 44d76ba9f..ac22ee4b1 100644 --- a/pkg/sentry/fs/gofer/inode_state.go +++ b/pkg/sentry/fs/gofer/inode_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/path.go b/pkg/sentry/fs/gofer/path.go index 8ae33d286..4cbf9e9d9 100644 --- a/pkg/sentry/fs/gofer/path.go +++ b/pkg/sentry/fs/gofer/path.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/session.go b/pkg/sentry/fs/gofer/session.go index 4ed688ce5..4cb65e7c6 100644 --- a/pkg/sentry/fs/gofer/session.go +++ b/pkg/sentry/fs/gofer/session.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/session_state.go b/pkg/sentry/fs/gofer/session_state.go index b1f299be5..68fbf3417 100644 --- a/pkg/sentry/fs/gofer/session_state.go +++ b/pkg/sentry/fs/gofer/session_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/socket.go b/pkg/sentry/fs/gofer/socket.go index ce6d3d5c3..cbd5b9a84 100644 --- a/pkg/sentry/fs/gofer/socket.go +++ b/pkg/sentry/fs/gofer/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/gofer/util.go b/pkg/sentry/fs/gofer/util.go index 1a759370d..d0e1096ce 100644 --- a/pkg/sentry/fs/gofer/util.go +++ b/pkg/sentry/fs/gofer/util.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/control.go b/pkg/sentry/fs/host/control.go index 0753640a2..480f0c8f4 100644 --- a/pkg/sentry/fs/host/control.go +++ b/pkg/sentry/fs/host/control.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/descriptor.go b/pkg/sentry/fs/host/descriptor.go index 554e1693a..ffcd57a94 100644 --- a/pkg/sentry/fs/host/descriptor.go +++ b/pkg/sentry/fs/host/descriptor.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/descriptor_state.go b/pkg/sentry/fs/host/descriptor_state.go index 530c0109f..8167390a9 100644 --- a/pkg/sentry/fs/host/descriptor_state.go +++ b/pkg/sentry/fs/host/descriptor_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/descriptor_test.go b/pkg/sentry/fs/host/descriptor_test.go index 5dec84ab2..ff08e43af 100644 --- a/pkg/sentry/fs/host/descriptor_test.go +++ b/pkg/sentry/fs/host/descriptor_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/device.go b/pkg/sentry/fs/host/device.go index b5adedf44..055024c44 100644 --- a/pkg/sentry/fs/host/device.go +++ b/pkg/sentry/fs/host/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/file.go b/pkg/sentry/fs/host/file.go index 2a8f285ff..82e2ae3b9 100644 --- a/pkg/sentry/fs/host/file.go +++ b/pkg/sentry/fs/host/file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/fs.go b/pkg/sentry/fs/host/fs.go index de349a41a..b1b8dc0b6 100644 --- a/pkg/sentry/fs/host/fs.go +++ b/pkg/sentry/fs/host/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/fs_test.go b/pkg/sentry/fs/host/fs_test.go index c83b29a16..16c89ddf1 100644 --- a/pkg/sentry/fs/host/fs_test.go +++ b/pkg/sentry/fs/host/fs_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/inode.go b/pkg/sentry/fs/host/inode.go index 69c648f67..20e077f77 100644 --- a/pkg/sentry/fs/host/inode.go +++ b/pkg/sentry/fs/host/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/inode_state.go b/pkg/sentry/fs/host/inode_state.go index b7c1a9581..26cc755bc 100644 --- a/pkg/sentry/fs/host/inode_state.go +++ b/pkg/sentry/fs/host/inode_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/inode_test.go b/pkg/sentry/fs/host/inode_test.go index 9f1561bd5..ad1878b5a 100644 --- a/pkg/sentry/fs/host/inode_test.go +++ b/pkg/sentry/fs/host/inode_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/ioctl_unsafe.go b/pkg/sentry/fs/host/ioctl_unsafe.go index 175dca613..b5a85c4d9 100644 --- a/pkg/sentry/fs/host/ioctl_unsafe.go +++ b/pkg/sentry/fs/host/ioctl_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/socket.go b/pkg/sentry/fs/host/socket.go index be2c3581f..3034e9441 100644 --- a/pkg/sentry/fs/host/socket.go +++ b/pkg/sentry/fs/host/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/socket_iovec.go b/pkg/sentry/fs/host/socket_iovec.go index d4ce4a8c1..5efbb3ae8 100644 --- a/pkg/sentry/fs/host/socket_iovec.go +++ b/pkg/sentry/fs/host/socket_iovec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/socket_state.go b/pkg/sentry/fs/host/socket_state.go index 2932c1f16..5676c451a 100644 --- a/pkg/sentry/fs/host/socket_state.go +++ b/pkg/sentry/fs/host/socket_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/socket_test.go b/pkg/sentry/fs/host/socket_test.go index 83e8e1b3c..cc760a7e1 100644 --- a/pkg/sentry/fs/host/socket_test.go +++ b/pkg/sentry/fs/host/socket_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/socket_unsafe.go b/pkg/sentry/fs/host/socket_unsafe.go index f35e2492d..8873705c0 100644 --- a/pkg/sentry/fs/host/socket_unsafe.go +++ b/pkg/sentry/fs/host/socket_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/tty.go b/pkg/sentry/fs/host/tty.go index c5cb75df7..e45b339f5 100644 --- a/pkg/sentry/fs/host/tty.go +++ b/pkg/sentry/fs/host/tty.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/util.go b/pkg/sentry/fs/host/util.go index 40c450660..94ff7708e 100644 --- a/pkg/sentry/fs/host/util.go +++ b/pkg/sentry/fs/host/util.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/util_unsafe.go b/pkg/sentry/fs/host/util_unsafe.go index a8721d197..b95a57c3f 100644 --- a/pkg/sentry/fs/host/util_unsafe.go +++ b/pkg/sentry/fs/host/util_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/host/wait_test.go b/pkg/sentry/fs/host/wait_test.go index 9ca8c399f..afcb74724 100644 --- a/pkg/sentry/fs/host/wait_test.go +++ b/pkg/sentry/fs/host/wait_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inode.go b/pkg/sentry/fs/inode.go index fe411a766..d764ef93d 100644 --- a/pkg/sentry/fs/inode.go +++ b/pkg/sentry/fs/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inode_inotify.go b/pkg/sentry/fs/inode_inotify.go index d2b653bc7..0f2a66a79 100644 --- a/pkg/sentry/fs/inode_inotify.go +++ b/pkg/sentry/fs/inode_inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inode_operations.go b/pkg/sentry/fs/inode_operations.go index ff8b75f31..ac287e1e4 100644 --- a/pkg/sentry/fs/inode_operations.go +++ b/pkg/sentry/fs/inode_operations.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inode_overlay.go b/pkg/sentry/fs/inode_overlay.go index bda3e1861..3d015328e 100644 --- a/pkg/sentry/fs/inode_overlay.go +++ b/pkg/sentry/fs/inode_overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inode_overlay_test.go b/pkg/sentry/fs/inode_overlay_test.go index fa8accf6c..66b3da2d0 100644 --- a/pkg/sentry/fs/inode_overlay_test.go +++ b/pkg/sentry/fs/inode_overlay_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inotify.go b/pkg/sentry/fs/inotify.go index 59fa662f3..2652582c3 100644 --- a/pkg/sentry/fs/inotify.go +++ b/pkg/sentry/fs/inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inotify_event.go b/pkg/sentry/fs/inotify_event.go index f09928b68..d52f956e4 100644 --- a/pkg/sentry/fs/inotify_event.go +++ b/pkg/sentry/fs/inotify_event.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/inotify_watch.go b/pkg/sentry/fs/inotify_watch.go index d33e7e498..a0b488467 100644 --- a/pkg/sentry/fs/inotify_watch.go +++ b/pkg/sentry/fs/inotify_watch.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/lock/lock.go b/pkg/sentry/fs/lock/lock.go index 5ff800d2d..f2aee4512 100644 --- a/pkg/sentry/fs/lock/lock.go +++ b/pkg/sentry/fs/lock/lock.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/lock/lock_range_test.go b/pkg/sentry/fs/lock/lock_range_test.go index b0ab882b9..6221199d1 100644 --- a/pkg/sentry/fs/lock/lock_range_test.go +++ b/pkg/sentry/fs/lock/lock_range_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/lock/lock_set_functions.go b/pkg/sentry/fs/lock/lock_set_functions.go index 395592a4b..8a3ace0c1 100644 --- a/pkg/sentry/fs/lock/lock_set_functions.go +++ b/pkg/sentry/fs/lock/lock_set_functions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/lock/lock_test.go b/pkg/sentry/fs/lock/lock_test.go index 67fa4b1dd..ba002aeb7 100644 --- a/pkg/sentry/fs/lock/lock_test.go +++ b/pkg/sentry/fs/lock/lock_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/mock.go b/pkg/sentry/fs/mock.go index 118e30f63..cf359a1f1 100644 --- a/pkg/sentry/fs/mock.go +++ b/pkg/sentry/fs/mock.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/mount.go b/pkg/sentry/fs/mount.go index 4d1693204..a169ea4c9 100644 --- a/pkg/sentry/fs/mount.go +++ b/pkg/sentry/fs/mount.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/mount_overlay.go b/pkg/sentry/fs/mount_overlay.go index fb60a1aec..535f812c8 100644 --- a/pkg/sentry/fs/mount_overlay.go +++ b/pkg/sentry/fs/mount_overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/mount_test.go b/pkg/sentry/fs/mount_test.go index d7605b2c9..9f7fbeff2 100644 --- a/pkg/sentry/fs/mount_test.go +++ b/pkg/sentry/fs/mount_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/mounts.go b/pkg/sentry/fs/mounts.go index f6f7be0aa..01eb4607e 100644 --- a/pkg/sentry/fs/mounts.go +++ b/pkg/sentry/fs/mounts.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/mounts_test.go b/pkg/sentry/fs/mounts_test.go index 54000614f..56d726dd1 100644 --- a/pkg/sentry/fs/mounts_test.go +++ b/pkg/sentry/fs/mounts_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/offset.go b/pkg/sentry/fs/offset.go index 38aee765a..3f68da149 100644 --- a/pkg/sentry/fs/offset.go +++ b/pkg/sentry/fs/offset.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/overlay.go b/pkg/sentry/fs/overlay.go index f3e2d5cbe..db89a5f70 100644 --- a/pkg/sentry/fs/overlay.go +++ b/pkg/sentry/fs/overlay.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/path.go b/pkg/sentry/fs/path.go index 52139b648..e4dc02dbb 100644 --- a/pkg/sentry/fs/path.go +++ b/pkg/sentry/fs/path.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/path_test.go b/pkg/sentry/fs/path_test.go index 4ba1498f6..e6f57ebba 100644 --- a/pkg/sentry/fs/path_test.go +++ b/pkg/sentry/fs/path_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/cpuinfo.go b/pkg/sentry/fs/proc/cpuinfo.go index f756c45bf..15031234e 100644 --- a/pkg/sentry/fs/proc/cpuinfo.go +++ b/pkg/sentry/fs/proc/cpuinfo.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/device/device.go b/pkg/sentry/fs/proc/device/device.go index 04b687bcf..0de466c73 100644 --- a/pkg/sentry/fs/proc/device/device.go +++ b/pkg/sentry/fs/proc/device/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/exec_args.go b/pkg/sentry/fs/proc/exec_args.go index fc21dfbbd..d49dad685 100644 --- a/pkg/sentry/fs/proc/exec_args.go +++ b/pkg/sentry/fs/proc/exec_args.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/fds.go b/pkg/sentry/fs/proc/fds.go index f2329e623..744b31c74 100644 --- a/pkg/sentry/fs/proc/fds.go +++ b/pkg/sentry/fs/proc/fds.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/filesystems.go b/pkg/sentry/fs/proc/filesystems.go index c050a00be..7bb081d0e 100644 --- a/pkg/sentry/fs/proc/filesystems.go +++ b/pkg/sentry/fs/proc/filesystems.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/fs.go b/pkg/sentry/fs/proc/fs.go index 666a2d054..7c5f8484a 100644 --- a/pkg/sentry/fs/proc/fs.go +++ b/pkg/sentry/fs/proc/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/inode.go b/pkg/sentry/fs/proc/inode.go index 8dde2ea46..b03807043 100644 --- a/pkg/sentry/fs/proc/inode.go +++ b/pkg/sentry/fs/proc/inode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/loadavg.go b/pkg/sentry/fs/proc/loadavg.go index 3ee0e570a..2dfe7089a 100644 --- a/pkg/sentry/fs/proc/loadavg.go +++ b/pkg/sentry/fs/proc/loadavg.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/meminfo.go b/pkg/sentry/fs/proc/meminfo.go index 75cbf3e77..d2b9b92c7 100644 --- a/pkg/sentry/fs/proc/meminfo.go +++ b/pkg/sentry/fs/proc/meminfo.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/mounts.go b/pkg/sentry/fs/proc/mounts.go index fe62b167b..37ed30724 100644 --- a/pkg/sentry/fs/proc/mounts.go +++ b/pkg/sentry/fs/proc/mounts.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/net.go b/pkg/sentry/fs/proc/net.go index d24b2d370..4a107c739 100644 --- a/pkg/sentry/fs/proc/net.go +++ b/pkg/sentry/fs/proc/net.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/net_test.go b/pkg/sentry/fs/proc/net_test.go index 94677cc1d..9aed5fdca 100644 --- a/pkg/sentry/fs/proc/net_test.go +++ b/pkg/sentry/fs/proc/net_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/proc.go b/pkg/sentry/fs/proc/proc.go index 64e1e1998..196fa5128 100644 --- a/pkg/sentry/fs/proc/proc.go +++ b/pkg/sentry/fs/proc/proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/rpcinet_proc.go b/pkg/sentry/fs/proc/rpcinet_proc.go index 81f64a28b..db53686f6 100644 --- a/pkg/sentry/fs/proc/rpcinet_proc.go +++ b/pkg/sentry/fs/proc/rpcinet_proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/seqfile/seqfile.go b/pkg/sentry/fs/proc/seqfile/seqfile.go index 0a0eb45e2..10ea1f55d 100644 --- a/pkg/sentry/fs/proc/seqfile/seqfile.go +++ b/pkg/sentry/fs/proc/seqfile/seqfile.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/seqfile/seqfile_test.go b/pkg/sentry/fs/proc/seqfile/seqfile_test.go index 35403ab7f..c4de565eb 100644 --- a/pkg/sentry/fs/proc/seqfile/seqfile_test.go +++ b/pkg/sentry/fs/proc/seqfile/seqfile_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/stat.go b/pkg/sentry/fs/proc/stat.go index 18bd8e9b6..397f9ec6b 100644 --- a/pkg/sentry/fs/proc/stat.go +++ b/pkg/sentry/fs/proc/stat.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/sys.go b/pkg/sentry/fs/proc/sys.go index a7bc9198e..b889ed625 100644 --- a/pkg/sentry/fs/proc/sys.go +++ b/pkg/sentry/fs/proc/sys.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/sys_net.go b/pkg/sentry/fs/proc/sys_net.go index 0ce77f04f..e49794a48 100644 --- a/pkg/sentry/fs/proc/sys_net.go +++ b/pkg/sentry/fs/proc/sys_net.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/sys_net_state.go b/pkg/sentry/fs/proc/sys_net_state.go index 5f481a1cf..6eba709c6 100644 --- a/pkg/sentry/fs/proc/sys_net_state.go +++ b/pkg/sentry/fs/proc/sys_net_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/sys_net_test.go b/pkg/sentry/fs/proc/sys_net_test.go index ea0d94fce..78135ba13 100644 --- a/pkg/sentry/fs/proc/sys_net_test.go +++ b/pkg/sentry/fs/proc/sys_net_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/task.go b/pkg/sentry/fs/proc/task.go index 9f65a8337..0f400e80f 100644 --- a/pkg/sentry/fs/proc/task.go +++ b/pkg/sentry/fs/proc/task.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/uid_gid_map.go b/pkg/sentry/fs/proc/uid_gid_map.go index d433632cf..d649da0f1 100644 --- a/pkg/sentry/fs/proc/uid_gid_map.go +++ b/pkg/sentry/fs/proc/uid_gid_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/uptime.go b/pkg/sentry/fs/proc/uptime.go index d7ae26fcf..1ddf9fafa 100644 --- a/pkg/sentry/fs/proc/uptime.go +++ b/pkg/sentry/fs/proc/uptime.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/proc/version.go b/pkg/sentry/fs/proc/version.go index 58e0c793c..a5479990c 100644 --- a/pkg/sentry/fs/proc/version.go +++ b/pkg/sentry/fs/proc/version.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ramfs/dir.go b/pkg/sentry/fs/ramfs/dir.go index c0400b67d..a6b6a5c33 100644 --- a/pkg/sentry/fs/ramfs/dir.go +++ b/pkg/sentry/fs/ramfs/dir.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ramfs/socket.go b/pkg/sentry/fs/ramfs/socket.go index 5bcb6c364..9406a07ca 100644 --- a/pkg/sentry/fs/ramfs/socket.go +++ b/pkg/sentry/fs/ramfs/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ramfs/symlink.go b/pkg/sentry/fs/ramfs/symlink.go index 35dabdad2..f7835fe05 100644 --- a/pkg/sentry/fs/ramfs/symlink.go +++ b/pkg/sentry/fs/ramfs/symlink.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ramfs/tree.go b/pkg/sentry/fs/ramfs/tree.go index c1ac8a78b..8c6b31f70 100644 --- a/pkg/sentry/fs/ramfs/tree.go +++ b/pkg/sentry/fs/ramfs/tree.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/ramfs/tree_test.go b/pkg/sentry/fs/ramfs/tree_test.go index 8bee9cfc1..27abeb6ba 100644 --- a/pkg/sentry/fs/ramfs/tree_test.go +++ b/pkg/sentry/fs/ramfs/tree_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/restore.go b/pkg/sentry/fs/restore.go index a6645b41e..f10168125 100644 --- a/pkg/sentry/fs/restore.go +++ b/pkg/sentry/fs/restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/save.go b/pkg/sentry/fs/save.go index 90988d385..2eaf6ab69 100644 --- a/pkg/sentry/fs/save.go +++ b/pkg/sentry/fs/save.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/seek.go b/pkg/sentry/fs/seek.go index 72f3fb632..0f43918ad 100644 --- a/pkg/sentry/fs/seek.go +++ b/pkg/sentry/fs/seek.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/sync.go b/pkg/sentry/fs/sync.go index 6dcc2fe8d..1fff8059c 100644 --- a/pkg/sentry/fs/sync.go +++ b/pkg/sentry/fs/sync.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/sys/device.go b/pkg/sentry/fs/sys/device.go index 38ecd0c18..128d3a9d9 100644 --- a/pkg/sentry/fs/sys/device.go +++ b/pkg/sentry/fs/sys/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/sys/devices.go b/pkg/sentry/fs/sys/devices.go index 8b728a4e4..db91de435 100644 --- a/pkg/sentry/fs/sys/devices.go +++ b/pkg/sentry/fs/sys/devices.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/sys/fs.go b/pkg/sentry/fs/sys/fs.go index 44ae43754..f0c2322e0 100644 --- a/pkg/sentry/fs/sys/fs.go +++ b/pkg/sentry/fs/sys/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/sys/sys.go b/pkg/sentry/fs/sys/sys.go index c5b56fe69..d20ef91fa 100644 --- a/pkg/sentry/fs/sys/sys.go +++ b/pkg/sentry/fs/sys/sys.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/timerfd/timerfd.go b/pkg/sentry/fs/timerfd/timerfd.go index ef9a08854..749961f51 100644 --- a/pkg/sentry/fs/timerfd/timerfd.go +++ b/pkg/sentry/fs/timerfd/timerfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tmpfs/device.go b/pkg/sentry/fs/tmpfs/device.go index aade93c26..179c3a46f 100644 --- a/pkg/sentry/fs/tmpfs/device.go +++ b/pkg/sentry/fs/tmpfs/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tmpfs/file_regular.go b/pkg/sentry/fs/tmpfs/file_regular.go index d0c9b8bea..1ef256511 100644 --- a/pkg/sentry/fs/tmpfs/file_regular.go +++ b/pkg/sentry/fs/tmpfs/file_regular.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tmpfs/file_test.go b/pkg/sentry/fs/tmpfs/file_test.go index 743061190..b44c06556 100644 --- a/pkg/sentry/fs/tmpfs/file_test.go +++ b/pkg/sentry/fs/tmpfs/file_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tmpfs/fs.go b/pkg/sentry/fs/tmpfs/fs.go index 8e44421b6..b7c29a4d1 100644 --- a/pkg/sentry/fs/tmpfs/fs.go +++ b/pkg/sentry/fs/tmpfs/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tmpfs/inode_file.go b/pkg/sentry/fs/tmpfs/inode_file.go index 4450e1363..f89d86c83 100644 --- a/pkg/sentry/fs/tmpfs/inode_file.go +++ b/pkg/sentry/fs/tmpfs/inode_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tmpfs/tmpfs.go b/pkg/sentry/fs/tmpfs/tmpfs.go index 5bb4922cb..832914453 100644 --- a/pkg/sentry/fs/tmpfs/tmpfs.go +++ b/pkg/sentry/fs/tmpfs/tmpfs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/dir.go b/pkg/sentry/fs/tty/dir.go index f8713471a..0fc777e67 100644 --- a/pkg/sentry/fs/tty/dir.go +++ b/pkg/sentry/fs/tty/dir.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/fs.go b/pkg/sentry/fs/tty/fs.go index a53448c47..701b2f7d9 100644 --- a/pkg/sentry/fs/tty/fs.go +++ b/pkg/sentry/fs/tty/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/line_discipline.go b/pkg/sentry/fs/tty/line_discipline.go index c4a364edb..20d29d130 100644 --- a/pkg/sentry/fs/tty/line_discipline.go +++ b/pkg/sentry/fs/tty/line_discipline.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/master.go b/pkg/sentry/fs/tty/master.go index e2686a074..45e167e5f 100644 --- a/pkg/sentry/fs/tty/master.go +++ b/pkg/sentry/fs/tty/master.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/queue.go b/pkg/sentry/fs/tty/queue.go index 5e88d84d9..11fb92be3 100644 --- a/pkg/sentry/fs/tty/queue.go +++ b/pkg/sentry/fs/tty/queue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/slave.go b/pkg/sentry/fs/tty/slave.go index ed080ca0f..0ae57a02c 100644 --- a/pkg/sentry/fs/tty/slave.go +++ b/pkg/sentry/fs/tty/slave.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/terminal.go b/pkg/sentry/fs/tty/terminal.go index 79f9d76d7..2b4160ba5 100644 --- a/pkg/sentry/fs/tty/terminal.go +++ b/pkg/sentry/fs/tty/terminal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/fs/tty/tty_test.go b/pkg/sentry/fs/tty/tty_test.go index ad535838f..d2e75a511 100644 --- a/pkg/sentry/fs/tty/tty_test.go +++ b/pkg/sentry/fs/tty/tty_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/hostcpu/getcpu_amd64.s b/pkg/sentry/hostcpu/getcpu_amd64.s index 409db1450..aa00316da 100644 --- a/pkg/sentry/hostcpu/getcpu_amd64.s +++ b/pkg/sentry/hostcpu/getcpu_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/hostcpu/hostcpu.go b/pkg/sentry/hostcpu/hostcpu.go index 3adc847bb..d78f78402 100644 --- a/pkg/sentry/hostcpu/hostcpu.go +++ b/pkg/sentry/hostcpu/hostcpu.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/hostcpu/hostcpu_test.go b/pkg/sentry/hostcpu/hostcpu_test.go index 38de0e1f6..7d6885c9e 100644 --- a/pkg/sentry/hostcpu/hostcpu_test.go +++ b/pkg/sentry/hostcpu/hostcpu_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/inet/context.go b/pkg/sentry/inet/context.go index d05e96f15..8550c4793 100644 --- a/pkg/sentry/inet/context.go +++ b/pkg/sentry/inet/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/inet/inet.go b/pkg/sentry/inet/inet.go index 8206377cc..7c104fd47 100644 --- a/pkg/sentry/inet/inet.go +++ b/pkg/sentry/inet/inet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/inet/test_stack.go b/pkg/sentry/inet/test_stack.go index 05c1a1792..624371eb6 100644 --- a/pkg/sentry/inet/test_stack.go +++ b/pkg/sentry/inet/test_stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/abstract_socket_namespace.go b/pkg/sentry/kernel/abstract_socket_namespace.go index 1ea2cee36..5ce52e66c 100644 --- a/pkg/sentry/kernel/abstract_socket_namespace.go +++ b/pkg/sentry/kernel/abstract_socket_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/auth.go b/pkg/sentry/kernel/auth/auth.go index 19f15fd36..847d121aa 100644 --- a/pkg/sentry/kernel/auth/auth.go +++ b/pkg/sentry/kernel/auth/auth.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/capability_set.go b/pkg/sentry/kernel/auth/capability_set.go index 88d6243aa..7a0c967cd 100644 --- a/pkg/sentry/kernel/auth/capability_set.go +++ b/pkg/sentry/kernel/auth/capability_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/context.go b/pkg/sentry/kernel/auth/context.go index f7e945599..16d110610 100644 --- a/pkg/sentry/kernel/auth/context.go +++ b/pkg/sentry/kernel/auth/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/credentials.go b/pkg/sentry/kernel/auth/credentials.go index 2055da196..1511a0324 100644 --- a/pkg/sentry/kernel/auth/credentials.go +++ b/pkg/sentry/kernel/auth/credentials.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/id.go b/pkg/sentry/kernel/auth/id.go index e5bed44d7..0a58ba17c 100644 --- a/pkg/sentry/kernel/auth/id.go +++ b/pkg/sentry/kernel/auth/id.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/id_map.go b/pkg/sentry/kernel/auth/id_map.go index 43f439825..e5d6028d6 100644 --- a/pkg/sentry/kernel/auth/id_map.go +++ b/pkg/sentry/kernel/auth/id_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/id_map_functions.go b/pkg/sentry/kernel/auth/id_map_functions.go index 8f1a189ec..432dbfb6d 100644 --- a/pkg/sentry/kernel/auth/id_map_functions.go +++ b/pkg/sentry/kernel/auth/id_map_functions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/auth/user_namespace.go b/pkg/sentry/kernel/auth/user_namespace.go index 159940a69..a40dd668f 100644 --- a/pkg/sentry/kernel/auth/user_namespace.go +++ b/pkg/sentry/kernel/auth/user_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/context.go b/pkg/sentry/kernel/context.go index b629521eb..a1a084eab 100644 --- a/pkg/sentry/kernel/context.go +++ b/pkg/sentry/kernel/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/contexttest/contexttest.go b/pkg/sentry/kernel/contexttest/contexttest.go index eb56a6a07..ae67e2a25 100644 --- a/pkg/sentry/kernel/contexttest/contexttest.go +++ b/pkg/sentry/kernel/contexttest/contexttest.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/epoll/epoll.go b/pkg/sentry/kernel/epoll/epoll.go index befefb11c..2399ae6f2 100644 --- a/pkg/sentry/kernel/epoll/epoll.go +++ b/pkg/sentry/kernel/epoll/epoll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/epoll/epoll_state.go b/pkg/sentry/kernel/epoll/epoll_state.go index f6e3e4825..4c3c38f9e 100644 --- a/pkg/sentry/kernel/epoll/epoll_state.go +++ b/pkg/sentry/kernel/epoll/epoll_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/epoll/epoll_test.go b/pkg/sentry/kernel/epoll/epoll_test.go index d89c1b745..49b781b69 100644 --- a/pkg/sentry/kernel/epoll/epoll_test.go +++ b/pkg/sentry/kernel/epoll/epoll_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/eventfd/eventfd.go b/pkg/sentry/kernel/eventfd/eventfd.go index b448ad813..5d3139eef 100644 --- a/pkg/sentry/kernel/eventfd/eventfd.go +++ b/pkg/sentry/kernel/eventfd/eventfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/eventfd/eventfd_test.go b/pkg/sentry/kernel/eventfd/eventfd_test.go index 14e8996d9..1159638e5 100644 --- a/pkg/sentry/kernel/eventfd/eventfd_test.go +++ b/pkg/sentry/kernel/eventfd/eventfd_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/fasync/fasync.go b/pkg/sentry/kernel/fasync/fasync.go index 298d988ea..84cd08501 100644 --- a/pkg/sentry/kernel/fasync/fasync.go +++ b/pkg/sentry/kernel/fasync/fasync.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/fd_map.go b/pkg/sentry/kernel/fd_map.go index 715f4714d..c5636d233 100644 --- a/pkg/sentry/kernel/fd_map.go +++ b/pkg/sentry/kernel/fd_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/fd_map_test.go b/pkg/sentry/kernel/fd_map_test.go index 9e76f0a2d..22db4c7cf 100644 --- a/pkg/sentry/kernel/fd_map_test.go +++ b/pkg/sentry/kernel/fd_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/fs_context.go b/pkg/sentry/kernel/fs_context.go index 3cf0db280..d8115f59a 100644 --- a/pkg/sentry/kernel/fs_context.go +++ b/pkg/sentry/kernel/fs_context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/futex/futex.go b/pkg/sentry/kernel/futex/futex.go index cd7d51621..bb38eb81e 100644 --- a/pkg/sentry/kernel/futex/futex.go +++ b/pkg/sentry/kernel/futex/futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/futex/futex_test.go b/pkg/sentry/kernel/futex/futex_test.go index 9d44ee8e5..2de5239bf 100644 --- a/pkg/sentry/kernel/futex/futex_test.go +++ b/pkg/sentry/kernel/futex/futex_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/ipc_namespace.go b/pkg/sentry/kernel/ipc_namespace.go index 9ceb9bd92..ebe12812c 100644 --- a/pkg/sentry/kernel/ipc_namespace.go +++ b/pkg/sentry/kernel/ipc_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/kdefs/kdefs.go b/pkg/sentry/kernel/kdefs/kdefs.go index 8eafe810b..304da2032 100644 --- a/pkg/sentry/kernel/kdefs/kdefs.go +++ b/pkg/sentry/kernel/kdefs/kdefs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index a1b2d7161..0468dd678 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/kernel_state.go b/pkg/sentry/kernel/kernel_state.go index aae6f9ad2..48c3ff5a9 100644 --- a/pkg/sentry/kernel/kernel_state.go +++ b/pkg/sentry/kernel/kernel_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/memevent/memory_events.go b/pkg/sentry/kernel/memevent/memory_events.go index d09d6debf..0e2cee807 100644 --- a/pkg/sentry/kernel/memevent/memory_events.go +++ b/pkg/sentry/kernel/memevent/memory_events.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/memevent/memory_events.proto b/pkg/sentry/kernel/memevent/memory_events.proto index 43b8deb76..bf8029ff5 100644 --- a/pkg/sentry/kernel/memevent/memory_events.proto +++ b/pkg/sentry/kernel/memevent/memory_events.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pending_signals.go b/pkg/sentry/kernel/pending_signals.go index deff6def9..c93f6598a 100644 --- a/pkg/sentry/kernel/pending_signals.go +++ b/pkg/sentry/kernel/pending_signals.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pending_signals_state.go b/pkg/sentry/kernel/pending_signals_state.go index 72be6702f..2c902c7e3 100644 --- a/pkg/sentry/kernel/pending_signals_state.go +++ b/pkg/sentry/kernel/pending_signals_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/buffers.go b/pkg/sentry/kernel/pipe/buffers.go index 54e059f8b..ba53fd482 100644 --- a/pkg/sentry/kernel/pipe/buffers.go +++ b/pkg/sentry/kernel/pipe/buffers.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/device.go b/pkg/sentry/kernel/pipe/device.go index eec5c5de8..eb59e15a1 100644 --- a/pkg/sentry/kernel/pipe/device.go +++ b/pkg/sentry/kernel/pipe/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/node.go b/pkg/sentry/kernel/pipe/node.go index 1336b6293..99188dddf 100644 --- a/pkg/sentry/kernel/pipe/node.go +++ b/pkg/sentry/kernel/pipe/node.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/node_test.go b/pkg/sentry/kernel/pipe/node_test.go index ad103b195..7ddecdad8 100644 --- a/pkg/sentry/kernel/pipe/node_test.go +++ b/pkg/sentry/kernel/pipe/node_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/pipe.go b/pkg/sentry/kernel/pipe/pipe.go index 357d1162e..bd7649d2f 100644 --- a/pkg/sentry/kernel/pipe/pipe.go +++ b/pkg/sentry/kernel/pipe/pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/pipe_test.go b/pkg/sentry/kernel/pipe/pipe_test.go index 3b9895927..de340c40c 100644 --- a/pkg/sentry/kernel/pipe/pipe_test.go +++ b/pkg/sentry/kernel/pipe/pipe_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/reader.go b/pkg/sentry/kernel/pipe/reader.go index f27379969..48fab45d1 100644 --- a/pkg/sentry/kernel/pipe/reader.go +++ b/pkg/sentry/kernel/pipe/reader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/reader_writer.go b/pkg/sentry/kernel/pipe/reader_writer.go index 1090432d7..ddcc5e09a 100644 --- a/pkg/sentry/kernel/pipe/reader_writer.go +++ b/pkg/sentry/kernel/pipe/reader_writer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/pipe/writer.go b/pkg/sentry/kernel/pipe/writer.go index 6fea9769c..0f29fbc43 100644 --- a/pkg/sentry/kernel/pipe/writer.go +++ b/pkg/sentry/kernel/pipe/writer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/posixtimer.go b/pkg/sentry/kernel/posixtimer.go index 40b5acca3..a016b4087 100644 --- a/pkg/sentry/kernel/posixtimer.go +++ b/pkg/sentry/kernel/posixtimer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/ptrace.go b/pkg/sentry/kernel/ptrace.go index 15f2e2964..4423e7efd 100644 --- a/pkg/sentry/kernel/ptrace.go +++ b/pkg/sentry/kernel/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/ptrace_amd64.go b/pkg/sentry/kernel/ptrace_amd64.go index 1f88efca3..048eeaa3f 100644 --- a/pkg/sentry/kernel/ptrace_amd64.go +++ b/pkg/sentry/kernel/ptrace_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google Inc. +// 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. diff --git a/pkg/sentry/kernel/ptrace_arm64.go b/pkg/sentry/kernel/ptrace_arm64.go index 4636405e6..4899c813f 100644 --- a/pkg/sentry/kernel/ptrace_arm64.go +++ b/pkg/sentry/kernel/ptrace_arm64.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google Inc. +// 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. diff --git a/pkg/sentry/kernel/rseq.go b/pkg/sentry/kernel/rseq.go index 6d3314e81..c4fb2c56c 100644 --- a/pkg/sentry/kernel/rseq.go +++ b/pkg/sentry/kernel/rseq.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/sched/cpuset.go b/pkg/sentry/kernel/sched/cpuset.go index 41ac1067d..c6c436690 100644 --- a/pkg/sentry/kernel/sched/cpuset.go +++ b/pkg/sentry/kernel/sched/cpuset.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/sched/cpuset_test.go b/pkg/sentry/kernel/sched/cpuset_test.go index a036ed513..3af9f1197 100644 --- a/pkg/sentry/kernel/sched/cpuset_test.go +++ b/pkg/sentry/kernel/sched/cpuset_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/sched/sched.go b/pkg/sentry/kernel/sched/sched.go index e59909baf..de18c9d02 100644 --- a/pkg/sentry/kernel/sched/sched.go +++ b/pkg/sentry/kernel/sched/sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/seccomp.go b/pkg/sentry/kernel/seccomp.go index 4bed4d373..cc75eb08a 100644 --- a/pkg/sentry/kernel/seccomp.go +++ b/pkg/sentry/kernel/seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/semaphore/semaphore.go b/pkg/sentry/kernel/semaphore/semaphore.go index 2b7c1a9bc..9d0620e02 100644 --- a/pkg/sentry/kernel/semaphore/semaphore.go +++ b/pkg/sentry/kernel/semaphore/semaphore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/semaphore/semaphore_test.go b/pkg/sentry/kernel/semaphore/semaphore_test.go index 2e51e6ee5..abfcd0fb4 100644 --- a/pkg/sentry/kernel/semaphore/semaphore_test.go +++ b/pkg/sentry/kernel/semaphore/semaphore_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/sessions.go b/pkg/sentry/kernel/sessions.go index 070c2f930..610e199da 100644 --- a/pkg/sentry/kernel/sessions.go +++ b/pkg/sentry/kernel/sessions.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/shm/device.go b/pkg/sentry/kernel/shm/device.go index bbc653ed8..3cb759072 100644 --- a/pkg/sentry/kernel/shm/device.go +++ b/pkg/sentry/kernel/shm/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/shm/shm.go b/pkg/sentry/kernel/shm/shm.go index d4812a065..00393b5f0 100644 --- a/pkg/sentry/kernel/shm/shm.go +++ b/pkg/sentry/kernel/shm/shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/signal.go b/pkg/sentry/kernel/signal.go index 22a56c6fc..b528ec0dc 100644 --- a/pkg/sentry/kernel/signal.go +++ b/pkg/sentry/kernel/signal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/signal_handlers.go b/pkg/sentry/kernel/signal_handlers.go index 60cbe85b8..ce8bcb5e5 100644 --- a/pkg/sentry/kernel/signal_handlers.go +++ b/pkg/sentry/kernel/signal_handlers.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index 293b21249..0572053db 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/syscalls_state.go b/pkg/sentry/kernel/syscalls_state.go index 981455d46..00358326b 100644 --- a/pkg/sentry/kernel/syscalls_state.go +++ b/pkg/sentry/kernel/syscalls_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/syslog.go b/pkg/sentry/kernel/syslog.go index 2aecf3eea..175d1b247 100644 --- a/pkg/sentry/kernel/syslog.go +++ b/pkg/sentry/kernel/syslog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/table_test.go b/pkg/sentry/kernel/table_test.go index 3b29d3c6a..8f7cdb9f3 100644 --- a/pkg/sentry/kernel/table_test.go +++ b/pkg/sentry/kernel/table_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go index ed2175c37..f9378c2de 100644 --- a/pkg/sentry/kernel/task.go +++ b/pkg/sentry/kernel/task.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_acct.go b/pkg/sentry/kernel/task_acct.go index 24230af89..1ca2a82eb 100644 --- a/pkg/sentry/kernel/task_acct.go +++ b/pkg/sentry/kernel/task_acct.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_block.go b/pkg/sentry/kernel/task_block.go index e5027e551..30a7f6b1e 100644 --- a/pkg/sentry/kernel/task_block.go +++ b/pkg/sentry/kernel/task_block.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_clone.go b/pkg/sentry/kernel/task_clone.go index daf974920..bba8ddd39 100644 --- a/pkg/sentry/kernel/task_clone.go +++ b/pkg/sentry/kernel/task_clone.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_context.go b/pkg/sentry/kernel/task_context.go index ac38dd157..bbd294141 100644 --- a/pkg/sentry/kernel/task_context.go +++ b/pkg/sentry/kernel/task_context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_exec.go b/pkg/sentry/kernel/task_exec.go index b49f902a5..5d1425d5c 100644 --- a/pkg/sentry/kernel/task_exec.go +++ b/pkg/sentry/kernel/task_exec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_exit.go b/pkg/sentry/kernel/task_exit.go index a07956208..6e9701b01 100644 --- a/pkg/sentry/kernel/task_exit.go +++ b/pkg/sentry/kernel/task_exit.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_futex.go b/pkg/sentry/kernel/task_futex.go index 351cf47d7..f98097c2c 100644 --- a/pkg/sentry/kernel/task_futex.go +++ b/pkg/sentry/kernel/task_futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_identity.go b/pkg/sentry/kernel/task_identity.go index 6c9608f8d..17f08729a 100644 --- a/pkg/sentry/kernel/task_identity.go +++ b/pkg/sentry/kernel/task_identity.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_log.go b/pkg/sentry/kernel/task_log.go index f4c881c2d..e0e57e8bd 100644 --- a/pkg/sentry/kernel/task_log.go +++ b/pkg/sentry/kernel/task_log.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_net.go b/pkg/sentry/kernel/task_net.go index fc7cefc1f..04c684c1a 100644 --- a/pkg/sentry/kernel/task_net.go +++ b/pkg/sentry/kernel/task_net.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_run.go b/pkg/sentry/kernel/task_run.go index 7115aa967..4549b437e 100644 --- a/pkg/sentry/kernel/task_run.go +++ b/pkg/sentry/kernel/task_run.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_sched.go b/pkg/sentry/kernel/task_sched.go index 3d654bf93..5455f6ea9 100644 --- a/pkg/sentry/kernel/task_sched.go +++ b/pkg/sentry/kernel/task_sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_signals.go b/pkg/sentry/kernel/task_signals.go index 7f2e0df72..654cf7525 100644 --- a/pkg/sentry/kernel/task_signals.go +++ b/pkg/sentry/kernel/task_signals.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_start.go b/pkg/sentry/kernel/task_start.go index b7534c0a2..b42531e57 100644 --- a/pkg/sentry/kernel/task_start.go +++ b/pkg/sentry/kernel/task_start.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_stop.go b/pkg/sentry/kernel/task_stop.go index 1302cadc1..e735a5dd0 100644 --- a/pkg/sentry/kernel/task_stop.go +++ b/pkg/sentry/kernel/task_stop.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_syscall.go b/pkg/sentry/kernel/task_syscall.go index 52f5fde8d..a9283d0df 100644 --- a/pkg/sentry/kernel/task_syscall.go +++ b/pkg/sentry/kernel/task_syscall.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_test.go b/pkg/sentry/kernel/task_test.go index 3f37f505d..b895361d0 100644 --- a/pkg/sentry/kernel/task_test.go +++ b/pkg/sentry/kernel/task_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/task_usermem.go b/pkg/sentry/kernel/task_usermem.go index cb68799d3..461bd7316 100644 --- a/pkg/sentry/kernel/task_usermem.go +++ b/pkg/sentry/kernel/task_usermem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/thread_group.go b/pkg/sentry/kernel/thread_group.go index 58f3a7ec9..8bd53928e 100644 --- a/pkg/sentry/kernel/thread_group.go +++ b/pkg/sentry/kernel/thread_group.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/threads.go b/pkg/sentry/kernel/threads.go index 4fd6cf4e2..656bbd46c 100644 --- a/pkg/sentry/kernel/threads.go +++ b/pkg/sentry/kernel/threads.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/time/context.go b/pkg/sentry/kernel/time/context.go index 3675ea20d..c0660d362 100644 --- a/pkg/sentry/kernel/time/context.go +++ b/pkg/sentry/kernel/time/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/time/time.go b/pkg/sentry/kernel/time/time.go index ca0f4ba2e..3846cf1ea 100644 --- a/pkg/sentry/kernel/time/time.go +++ b/pkg/sentry/kernel/time/time.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/timekeeper.go b/pkg/sentry/kernel/timekeeper.go index d7bd85e78..505a4fa4f 100644 --- a/pkg/sentry/kernel/timekeeper.go +++ b/pkg/sentry/kernel/timekeeper.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/timekeeper_state.go b/pkg/sentry/kernel/timekeeper_state.go index f3a3ed543..6ce358a05 100644 --- a/pkg/sentry/kernel/timekeeper_state.go +++ b/pkg/sentry/kernel/timekeeper_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/timekeeper_test.go b/pkg/sentry/kernel/timekeeper_test.go index 6084bcb18..a92ad689e 100644 --- a/pkg/sentry/kernel/timekeeper_test.go +++ b/pkg/sentry/kernel/timekeeper_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/uncaught_signal.proto b/pkg/sentry/kernel/uncaught_signal.proto index c7f6a1978..0bdb062cb 100644 --- a/pkg/sentry/kernel/uncaught_signal.proto +++ b/pkg/sentry/kernel/uncaught_signal.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/uts_namespace.go b/pkg/sentry/kernel/uts_namespace.go index ed5f0c031..96fe3cbb9 100644 --- a/pkg/sentry/kernel/uts_namespace.go +++ b/pkg/sentry/kernel/uts_namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/vdso.go b/pkg/sentry/kernel/vdso.go index 3a35f1d00..d40ad74f4 100644 --- a/pkg/sentry/kernel/vdso.go +++ b/pkg/sentry/kernel/vdso.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/kernel/version.go b/pkg/sentry/kernel/version.go index 8d2f14209..5640dd71d 100644 --- a/pkg/sentry/kernel/version.go +++ b/pkg/sentry/kernel/version.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/limits/context.go b/pkg/sentry/limits/context.go index bf413eb7d..9200edb52 100644 --- a/pkg/sentry/limits/context.go +++ b/pkg/sentry/limits/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/limits/limits.go b/pkg/sentry/limits/limits.go index b0571739f..b6c22656b 100644 --- a/pkg/sentry/limits/limits.go +++ b/pkg/sentry/limits/limits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/limits/limits_test.go b/pkg/sentry/limits/limits_test.go index 945428163..658a20f56 100644 --- a/pkg/sentry/limits/limits_test.go +++ b/pkg/sentry/limits/limits_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/limits/linux.go b/pkg/sentry/limits/linux.go index e09d0d2fb..a2b401e3d 100644 --- a/pkg/sentry/limits/linux.go +++ b/pkg/sentry/limits/linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/loader/elf.go b/pkg/sentry/loader/elf.go index 385ad0102..97e32c8ba 100644 --- a/pkg/sentry/loader/elf.go +++ b/pkg/sentry/loader/elf.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/loader/interpreter.go b/pkg/sentry/loader/interpreter.go index 35b83654d..b88062ae5 100644 --- a/pkg/sentry/loader/interpreter.go +++ b/pkg/sentry/loader/interpreter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/loader/loader.go b/pkg/sentry/loader/loader.go index 79051befa..dc1a52398 100644 --- a/pkg/sentry/loader/loader.go +++ b/pkg/sentry/loader/loader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/loader/vdso.go b/pkg/sentry/loader/vdso.go index 8c196df84..207d8ed3d 100644 --- a/pkg/sentry/loader/vdso.go +++ b/pkg/sentry/loader/vdso.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/loader/vdso_state.go b/pkg/sentry/loader/vdso_state.go index b327f0e1e..db378e90a 100644 --- a/pkg/sentry/loader/vdso_state.go +++ b/pkg/sentry/loader/vdso_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/memmap/mapping_set.go b/pkg/sentry/memmap/mapping_set.go index bd07e9aac..3cf2b338f 100644 --- a/pkg/sentry/memmap/mapping_set.go +++ b/pkg/sentry/memmap/mapping_set.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/memmap/mapping_set_test.go b/pkg/sentry/memmap/mapping_set_test.go index 45d1d4688..c702555ce 100644 --- a/pkg/sentry/memmap/mapping_set_test.go +++ b/pkg/sentry/memmap/mapping_set_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/memmap/memmap.go b/pkg/sentry/memmap/memmap.go index 3f6f7ebd0..0106c857d 100644 --- a/pkg/sentry/memmap/memmap.go +++ b/pkg/sentry/memmap/memmap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/memutil/memutil.go b/pkg/sentry/memutil/memutil.go index 286d50ca4..a4154c42a 100644 --- a/pkg/sentry/memutil/memutil.go +++ b/pkg/sentry/memutil/memutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/memutil/memutil_unsafe.go b/pkg/sentry/memutil/memutil_unsafe.go index bc2c72f55..92eab8a26 100644 --- a/pkg/sentry/memutil/memutil_unsafe.go +++ b/pkg/sentry/memutil/memutil_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/address_space.go b/pkg/sentry/mm/address_space.go index 4dddcf7b5..06f587fde 100644 --- a/pkg/sentry/mm/address_space.go +++ b/pkg/sentry/mm/address_space.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/aio_context.go b/pkg/sentry/mm/aio_context.go index 7075792e0..5c61acf36 100644 --- a/pkg/sentry/mm/aio_context.go +++ b/pkg/sentry/mm/aio_context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/aio_context_state.go b/pkg/sentry/mm/aio_context_state.go index 192a6f744..c37fc9f7b 100644 --- a/pkg/sentry/mm/aio_context_state.go +++ b/pkg/sentry/mm/aio_context_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/debug.go b/pkg/sentry/mm/debug.go index d075ee1ca..fe58cfc4c 100644 --- a/pkg/sentry/mm/debug.go +++ b/pkg/sentry/mm/debug.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/io.go b/pkg/sentry/mm/io.go index 81787a6fd..e4c057d28 100644 --- a/pkg/sentry/mm/io.go +++ b/pkg/sentry/mm/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/lifecycle.go b/pkg/sentry/mm/lifecycle.go index 2fe03172c..e6aa6f9ef 100644 --- a/pkg/sentry/mm/lifecycle.go +++ b/pkg/sentry/mm/lifecycle.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/metadata.go b/pkg/sentry/mm/metadata.go index 5ef1ba0b1..9768e51f1 100644 --- a/pkg/sentry/mm/metadata.go +++ b/pkg/sentry/mm/metadata.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/mm.go b/pkg/sentry/mm/mm.go index a3417a46e..d25aa5136 100644 --- a/pkg/sentry/mm/mm.go +++ b/pkg/sentry/mm/mm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/mm_test.go b/pkg/sentry/mm/mm_test.go index ae4fba478..f4917419f 100644 --- a/pkg/sentry/mm/mm_test.go +++ b/pkg/sentry/mm/mm_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/pma.go b/pkg/sentry/mm/pma.go index 0cca743ef..ece561ff0 100644 --- a/pkg/sentry/mm/pma.go +++ b/pkg/sentry/mm/pma.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/procfs.go b/pkg/sentry/mm/procfs.go index 7cdbf6e25..c8302a553 100644 --- a/pkg/sentry/mm/procfs.go +++ b/pkg/sentry/mm/procfs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/save_restore.go b/pkg/sentry/mm/save_restore.go index 46e0e0754..0385957bd 100644 --- a/pkg/sentry/mm/save_restore.go +++ b/pkg/sentry/mm/save_restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/shm.go b/pkg/sentry/mm/shm.go index 3bc48c7e7..12913007b 100644 --- a/pkg/sentry/mm/shm.go +++ b/pkg/sentry/mm/shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/special_mappable.go b/pkg/sentry/mm/special_mappable.go index 3b5161998..687959005 100644 --- a/pkg/sentry/mm/special_mappable.go +++ b/pkg/sentry/mm/special_mappable.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/syscalls.go b/pkg/sentry/mm/syscalls.go index 7b675b9b5..a25318abb 100644 --- a/pkg/sentry/mm/syscalls.go +++ b/pkg/sentry/mm/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/mm/vma.go b/pkg/sentry/mm/vma.go index 931995254..ad901344b 100644 --- a/pkg/sentry/mm/vma.go +++ b/pkg/sentry/mm/vma.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/pgalloc/context.go b/pkg/sentry/pgalloc/context.go index adc97e78f..cb9809b1f 100644 --- a/pkg/sentry/pgalloc/context.go +++ b/pkg/sentry/pgalloc/context.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google Inc. +// 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. diff --git a/pkg/sentry/pgalloc/pgalloc.go b/pkg/sentry/pgalloc/pgalloc.go index 0754e608f..411dafa07 100644 --- a/pkg/sentry/pgalloc/pgalloc.go +++ b/pkg/sentry/pgalloc/pgalloc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/pgalloc/pgalloc_test.go b/pkg/sentry/pgalloc/pgalloc_test.go index 726623c1a..14a39bb9e 100644 --- a/pkg/sentry/pgalloc/pgalloc_test.go +++ b/pkg/sentry/pgalloc/pgalloc_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/pgalloc/pgalloc_unsafe.go b/pkg/sentry/pgalloc/pgalloc_unsafe.go index 33b0a68a8..a4b5d581c 100644 --- a/pkg/sentry/pgalloc/pgalloc_unsafe.go +++ b/pkg/sentry/pgalloc/pgalloc_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/pgalloc/save_restore.go b/pkg/sentry/pgalloc/save_restore.go index 21024e656..cf169af55 100644 --- a/pkg/sentry/pgalloc/save_restore.go +++ b/pkg/sentry/pgalloc/save_restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/context.go b/pkg/sentry/platform/context.go index cca21a23e..793f57fd7 100644 --- a/pkg/sentry/platform/context.go +++ b/pkg/sentry/platform/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/interrupt/interrupt.go b/pkg/sentry/platform/interrupt/interrupt.go index 9c83f41eb..a4651f500 100644 --- a/pkg/sentry/platform/interrupt/interrupt.go +++ b/pkg/sentry/platform/interrupt/interrupt.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/interrupt/interrupt_test.go b/pkg/sentry/platform/interrupt/interrupt_test.go index fb3284395..0ecdf6e7a 100644 --- a/pkg/sentry/platform/interrupt/interrupt_test.go +++ b/pkg/sentry/platform/interrupt/interrupt_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/address_space.go b/pkg/sentry/platform/kvm/address_space.go index f2f7ab1e8..689122175 100644 --- a/pkg/sentry/platform/kvm/address_space.go +++ b/pkg/sentry/platform/kvm/address_space.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/allocator.go b/pkg/sentry/platform/kvm/allocator.go index b25cad155..42bcc9733 100644 --- a/pkg/sentry/platform/kvm/allocator.go +++ b/pkg/sentry/platform/kvm/allocator.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/bluepill.go b/pkg/sentry/platform/kvm/bluepill.go index f24f1c662..a926e6f8b 100644 --- a/pkg/sentry/platform/kvm/bluepill.go +++ b/pkg/sentry/platform/kvm/bluepill.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.go b/pkg/sentry/platform/kvm/bluepill_amd64.go index 6520682d7..c258408f9 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64.go +++ b/pkg/sentry/platform/kvm/bluepill_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.s b/pkg/sentry/platform/kvm/bluepill_amd64.s index 65b01f358..2bc34a435 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64.s +++ b/pkg/sentry/platform/kvm/bluepill_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go index 21de2488e..92fde7ee0 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/bluepill_fault.go b/pkg/sentry/platform/kvm/bluepill_fault.go index e79a30ef2..3c452f5ba 100644 --- a/pkg/sentry/platform/kvm/bluepill_fault.go +++ b/pkg/sentry/platform/kvm/bluepill_fault.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/bluepill_unsafe.go b/pkg/sentry/platform/kvm/bluepill_unsafe.go index 2605f8c93..4184939e5 100644 --- a/pkg/sentry/platform/kvm/bluepill_unsafe.go +++ b/pkg/sentry/platform/kvm/bluepill_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/context.go b/pkg/sentry/platform/kvm/context.go index c75a4b415..0eb0020f7 100644 --- a/pkg/sentry/platform/kvm/context.go +++ b/pkg/sentry/platform/kvm/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/kvm.go b/pkg/sentry/platform/kvm/kvm.go index c5a4435b1..ed0521c3f 100644 --- a/pkg/sentry/platform/kvm/kvm.go +++ b/pkg/sentry/platform/kvm/kvm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/kvm_amd64.go b/pkg/sentry/platform/kvm/kvm_amd64.go index 70d0ac63b..61493ccaf 100644 --- a/pkg/sentry/platform/kvm/kvm_amd64.go +++ b/pkg/sentry/platform/kvm/kvm_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go b/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go index d0f6bb225..46c4b9113 100644 --- a/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/kvm_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/kvm_const.go b/pkg/sentry/platform/kvm/kvm_const.go index cac8d9937..d05f05c29 100644 --- a/pkg/sentry/platform/kvm/kvm_const.go +++ b/pkg/sentry/platform/kvm/kvm_const.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/kvm_test.go b/pkg/sentry/platform/kvm/kvm_test.go index 361200622..e83db71e9 100644 --- a/pkg/sentry/platform/kvm/kvm_test.go +++ b/pkg/sentry/platform/kvm/kvm_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/machine.go b/pkg/sentry/platform/kvm/machine.go index b8b3c9a4a..f5953b96e 100644 --- a/pkg/sentry/platform/kvm/machine.go +++ b/pkg/sentry/platform/kvm/machine.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/machine_amd64.go b/pkg/sentry/platform/kvm/machine_amd64.go index ccfe837b5..b6821122a 100644 --- a/pkg/sentry/platform/kvm/machine_amd64.go +++ b/pkg/sentry/platform/kvm/machine_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go index 69ba67ced..06a2e3b0c 100644 --- a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/machine_unsafe.go b/pkg/sentry/platform/kvm/machine_unsafe.go index 22ae60b63..452d88d7f 100644 --- a/pkg/sentry/platform/kvm/machine_unsafe.go +++ b/pkg/sentry/platform/kvm/machine_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/physical_map.go b/pkg/sentry/platform/kvm/physical_map.go index 9d7dca5b3..450eb8201 100644 --- a/pkg/sentry/platform/kvm/physical_map.go +++ b/pkg/sentry/platform/kvm/physical_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/testutil/testutil.go b/pkg/sentry/platform/kvm/testutil/testutil.go index 0d496561d..6cf2359a3 100644 --- a/pkg/sentry/platform/kvm/testutil/testutil.go +++ b/pkg/sentry/platform/kvm/testutil/testutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/testutil/testutil_amd64.go b/pkg/sentry/platform/kvm/testutil/testutil_amd64.go index fcba33813..203d71528 100644 --- a/pkg/sentry/platform/kvm/testutil/testutil_amd64.go +++ b/pkg/sentry/platform/kvm/testutil/testutil_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/testutil/testutil_amd64.s b/pkg/sentry/platform/kvm/testutil/testutil_amd64.s index f1da41a44..491ec0c2a 100644 --- a/pkg/sentry/platform/kvm/testutil/testutil_amd64.s +++ b/pkg/sentry/platform/kvm/testutil/testutil_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/virtual_map.go b/pkg/sentry/platform/kvm/virtual_map.go index 0343e9267..28a1b4414 100644 --- a/pkg/sentry/platform/kvm/virtual_map.go +++ b/pkg/sentry/platform/kvm/virtual_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/kvm/virtual_map_test.go b/pkg/sentry/platform/kvm/virtual_map_test.go index 935e0eb93..d03ec654a 100644 --- a/pkg/sentry/platform/kvm/virtual_map_test.go +++ b/pkg/sentry/platform/kvm/virtual_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/mmap_min_addr.go b/pkg/sentry/platform/mmap_min_addr.go index 1bcc1f8e9..90976735b 100644 --- a/pkg/sentry/platform/mmap_min_addr.go +++ b/pkg/sentry/platform/mmap_min_addr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/platform.go b/pkg/sentry/platform/platform.go index 0e48417b9..ae37276ad 100644 --- a/pkg/sentry/platform/platform.go +++ b/pkg/sentry/platform/platform.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/procid/procid.go b/pkg/sentry/platform/procid/procid.go index 3f49ab093..78b92422c 100644 --- a/pkg/sentry/platform/procid/procid.go +++ b/pkg/sentry/platform/procid/procid.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/procid/procid_amd64.s b/pkg/sentry/platform/procid/procid_amd64.s index ef3439c03..272c9fc14 100644 --- a/pkg/sentry/platform/procid/procid_amd64.s +++ b/pkg/sentry/platform/procid/procid_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/procid/procid_arm64.s b/pkg/sentry/platform/procid/procid_arm64.s index 02e907b6b..7a1684a18 100644 --- a/pkg/sentry/platform/procid/procid_arm64.s +++ b/pkg/sentry/platform/procid/procid_arm64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/procid/procid_net_test.go b/pkg/sentry/platform/procid/procid_net_test.go index e8dcc479d..b628e2285 100644 --- a/pkg/sentry/platform/procid/procid_net_test.go +++ b/pkg/sentry/platform/procid/procid_net_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/procid/procid_test.go b/pkg/sentry/platform/procid/procid_test.go index 7a57c7cdc..88dd0b3ae 100644 --- a/pkg/sentry/platform/procid/procid_test.go +++ b/pkg/sentry/platform/procid/procid_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/ptrace.go b/pkg/sentry/platform/ptrace/ptrace.go index 3c0713e95..6a890dd81 100644 --- a/pkg/sentry/platform/ptrace/ptrace.go +++ b/pkg/sentry/platform/ptrace/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/ptrace_unsafe.go b/pkg/sentry/platform/ptrace/ptrace_unsafe.go index 223b23199..585f6c1fb 100644 --- a/pkg/sentry/platform/ptrace/ptrace_unsafe.go +++ b/pkg/sentry/platform/ptrace/ptrace_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/stub_amd64.s b/pkg/sentry/platform/ptrace/stub_amd64.s index 63f98e40d..64c718d21 100644 --- a/pkg/sentry/platform/ptrace/stub_amd64.s +++ b/pkg/sentry/platform/ptrace/stub_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/stub_unsafe.go b/pkg/sentry/platform/ptrace/stub_unsafe.go index 48c16c4a1..54d5021a9 100644 --- a/pkg/sentry/platform/ptrace/stub_unsafe.go +++ b/pkg/sentry/platform/ptrace/stub_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/subprocess.go b/pkg/sentry/platform/ptrace/subprocess.go index 2a5d699ec..83b43057f 100644 --- a/pkg/sentry/platform/ptrace/subprocess.go +++ b/pkg/sentry/platform/ptrace/subprocess.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/subprocess_amd64.go b/pkg/sentry/platform/ptrace/subprocess_amd64.go index d23a1133e..77a0e908f 100644 --- a/pkg/sentry/platform/ptrace/subprocess_amd64.go +++ b/pkg/sentry/platform/ptrace/subprocess_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/subprocess_linux.go b/pkg/sentry/platform/ptrace/subprocess_linux.go index e2aab8135..2c07b4ac3 100644 --- a/pkg/sentry/platform/ptrace/subprocess_linux.go +++ b/pkg/sentry/platform/ptrace/subprocess_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go b/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go index 0c9263060..1bf7eab28 100644 --- a/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go +++ b/pkg/sentry/platform/ptrace/subprocess_linux_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ptrace/subprocess_unsafe.go b/pkg/sentry/platform/ptrace/subprocess_unsafe.go index ca6c4ac97..17736b05b 100644 --- a/pkg/sentry/platform/ptrace/subprocess_unsafe.go +++ b/pkg/sentry/platform/ptrace/subprocess_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/defs.go b/pkg/sentry/platform/ring0/defs.go index 98d0a6de0..5bbd4612d 100644 --- a/pkg/sentry/platform/ring0/defs.go +++ b/pkg/sentry/platform/ring0/defs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/defs_amd64.go b/pkg/sentry/platform/ring0/defs_amd64.go index 67242b92b..413c3dbc4 100644 --- a/pkg/sentry/platform/ring0/defs_amd64.go +++ b/pkg/sentry/platform/ring0/defs_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/entry_amd64.go b/pkg/sentry/platform/ring0/entry_amd64.go index 4a9affe64..a5ce67885 100644 --- a/pkg/sentry/platform/ring0/entry_amd64.go +++ b/pkg/sentry/platform/ring0/entry_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/entry_amd64.s b/pkg/sentry/platform/ring0/entry_amd64.s index afb040a6f..8cb8c4996 100644 --- a/pkg/sentry/platform/ring0/entry_amd64.s +++ b/pkg/sentry/platform/ring0/entry_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/gen_offsets/main.go b/pkg/sentry/platform/ring0/gen_offsets/main.go index 11c49855f..a4927da2f 100644 --- a/pkg/sentry/platform/ring0/gen_offsets/main.go +++ b/pkg/sentry/platform/ring0/gen_offsets/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/kernel.go b/pkg/sentry/platform/ring0/kernel.go index 19ac6eb7c..900c0bba7 100644 --- a/pkg/sentry/platform/ring0/kernel.go +++ b/pkg/sentry/platform/ring0/kernel.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/kernel_amd64.go b/pkg/sentry/platform/ring0/kernel_amd64.go index 5ed4342dd..3577b5127 100644 --- a/pkg/sentry/platform/ring0/kernel_amd64.go +++ b/pkg/sentry/platform/ring0/kernel_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/kernel_unsafe.go b/pkg/sentry/platform/ring0/kernel_unsafe.go index faf4240e5..16955ad91 100644 --- a/pkg/sentry/platform/ring0/kernel_unsafe.go +++ b/pkg/sentry/platform/ring0/kernel_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/lib_amd64.go b/pkg/sentry/platform/ring0/lib_amd64.go index 2b95a0141..9c5f26962 100644 --- a/pkg/sentry/platform/ring0/lib_amd64.go +++ b/pkg/sentry/platform/ring0/lib_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/lib_amd64.s b/pkg/sentry/platform/ring0/lib_amd64.s index 98a130525..75d742750 100644 --- a/pkg/sentry/platform/ring0/lib_amd64.s +++ b/pkg/sentry/platform/ring0/lib_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/offsets_amd64.go b/pkg/sentry/platform/ring0/offsets_amd64.go index 806e07ec0..85cc3fdad 100644 --- a/pkg/sentry/platform/ring0/offsets_amd64.go +++ b/pkg/sentry/platform/ring0/offsets_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/allocator.go b/pkg/sentry/platform/ring0/pagetables/allocator.go index ee6e90a11..23fd5c352 100644 --- a/pkg/sentry/platform/ring0/pagetables/allocator.go +++ b/pkg/sentry/platform/ring0/pagetables/allocator.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go b/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go index f48647b3a..1b996b4e2 100644 --- a/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go +++ b/pkg/sentry/platform/ring0/pagetables/allocator_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables.go b/pkg/sentry/platform/ring0/pagetables/pagetables.go index c7207ec18..e5dcaada7 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go index 746f614e5..7aa6c524e 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go index 2f82c4353..a1ec4b109 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_amd64_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_test.go b/pkg/sentry/platform/ring0/pagetables/pagetables_test.go index 3e5dc7dc7..36e424495 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_test.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go b/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go index 6bd8c3584..ff427fbe9 100644 --- a/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go +++ b/pkg/sentry/platform/ring0/pagetables/pagetables_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/pcids_x86.go b/pkg/sentry/platform/ring0/pagetables/pcids_x86.go index 0d9a51aa5..0f029f25d 100644 --- a/pkg/sentry/platform/ring0/pagetables/pcids_x86.go +++ b/pkg/sentry/platform/ring0/pagetables/pcids_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/pagetables/walker_amd64.go b/pkg/sentry/platform/ring0/pagetables/walker_amd64.go index c4c71d23e..8f9dacd93 100644 --- a/pkg/sentry/platform/ring0/pagetables/walker_amd64.go +++ b/pkg/sentry/platform/ring0/pagetables/walker_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/ring0.go b/pkg/sentry/platform/ring0/ring0.go index 10c51e88d..cdeb1b43a 100644 --- a/pkg/sentry/platform/ring0/ring0.go +++ b/pkg/sentry/platform/ring0/ring0.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/ring0/x86.go b/pkg/sentry/platform/ring0/x86.go index 4c6daec22..7e5ceafdb 100644 --- a/pkg/sentry/platform/ring0/x86.go +++ b/pkg/sentry/platform/ring0/x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/safecopy/atomic_amd64.s b/pkg/sentry/platform/safecopy/atomic_amd64.s index f90b4bfd1..a0cd78f33 100644 --- a/pkg/sentry/platform/safecopy/atomic_amd64.s +++ b/pkg/sentry/platform/safecopy/atomic_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/safecopy/safecopy.go b/pkg/sentry/platform/safecopy/safecopy.go index 69c66a3b7..5126871eb 100644 --- a/pkg/sentry/platform/safecopy/safecopy.go +++ b/pkg/sentry/platform/safecopy/safecopy.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/safecopy/safecopy_test.go b/pkg/sentry/platform/safecopy/safecopy_test.go index 1a682d28a..5818f7f9b 100644 --- a/pkg/sentry/platform/safecopy/safecopy_test.go +++ b/pkg/sentry/platform/safecopy/safecopy_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/safecopy/safecopy_unsafe.go b/pkg/sentry/platform/safecopy/safecopy_unsafe.go index f84527484..eef028e68 100644 --- a/pkg/sentry/platform/safecopy/safecopy_unsafe.go +++ b/pkg/sentry/platform/safecopy/safecopy_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/safecopy/sighandler_amd64.s b/pkg/sentry/platform/safecopy/sighandler_amd64.s index db7701a29..475ae48e9 100644 --- a/pkg/sentry/platform/safecopy/sighandler_amd64.s +++ b/pkg/sentry/platform/safecopy/sighandler_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/platform/safecopy/sighandler_arm64.s b/pkg/sentry/platform/safecopy/sighandler_arm64.s index cdfca8207..53e4ac2c1 100644 --- a/pkg/sentry/platform/safecopy/sighandler_arm64.s +++ b/pkg/sentry/platform/safecopy/sighandler_arm64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/safemem/block_unsafe.go b/pkg/sentry/safemem/block_unsafe.go index c3a9780d2..1f72deb61 100644 --- a/pkg/sentry/safemem/block_unsafe.go +++ b/pkg/sentry/safemem/block_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/safemem/io.go b/pkg/sentry/safemem/io.go index 6cb52439f..5c3d73eb7 100644 --- a/pkg/sentry/safemem/io.go +++ b/pkg/sentry/safemem/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/safemem/io_test.go b/pkg/sentry/safemem/io_test.go index 2eda8c3bb..629741bee 100644 --- a/pkg/sentry/safemem/io_test.go +++ b/pkg/sentry/safemem/io_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/safemem/safemem.go b/pkg/sentry/safemem/safemem.go index 090932d3e..3e70d33a2 100644 --- a/pkg/sentry/safemem/safemem.go +++ b/pkg/sentry/safemem/safemem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/safemem/seq_test.go b/pkg/sentry/safemem/seq_test.go index fddcaf714..eba4bb535 100644 --- a/pkg/sentry/safemem/seq_test.go +++ b/pkg/sentry/safemem/seq_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/safemem/seq_unsafe.go b/pkg/sentry/safemem/seq_unsafe.go index 83a6b7183..354a95dde 100644 --- a/pkg/sentry/safemem/seq_unsafe.go +++ b/pkg/sentry/safemem/seq_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/sighandling/sighandling.go b/pkg/sentry/sighandling/sighandling.go index 571245ce5..659b43363 100644 --- a/pkg/sentry/sighandling/sighandling.go +++ b/pkg/sentry/sighandling/sighandling.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/sighandling/sighandling_unsafe.go b/pkg/sentry/sighandling/sighandling_unsafe.go index db6e71487..aca77888a 100644 --- a/pkg/sentry/sighandling/sighandling_unsafe.go +++ b/pkg/sentry/sighandling/sighandling_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/control/control.go b/pkg/sentry/socket/control/control.go index d44f5e88a..abda364c9 100644 --- a/pkg/sentry/socket/control/control.go +++ b/pkg/sentry/socket/control/control.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/epsocket/device.go b/pkg/sentry/socket/epsocket/device.go index 3cc138eb0..ab4083efe 100644 --- a/pkg/sentry/socket/epsocket/device.go +++ b/pkg/sentry/socket/epsocket/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go index 768fa0dfa..520d82f68 100644 --- a/pkg/sentry/socket/epsocket/epsocket.go +++ b/pkg/sentry/socket/epsocket/epsocket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/epsocket/provider.go b/pkg/sentry/socket/epsocket/provider.go index 0d9c2df24..5a89a63fb 100644 --- a/pkg/sentry/socket/epsocket/provider.go +++ b/pkg/sentry/socket/epsocket/provider.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/epsocket/save_restore.go b/pkg/sentry/socket/epsocket/save_restore.go index f19afb6c0..feaafb7cc 100644 --- a/pkg/sentry/socket/epsocket/save_restore.go +++ b/pkg/sentry/socket/epsocket/save_restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/epsocket/stack.go b/pkg/sentry/socket/epsocket/stack.go index 37c48f4bc..edefa225b 100644 --- a/pkg/sentry/socket/epsocket/stack.go +++ b/pkg/sentry/socket/epsocket/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/hostinet/device.go b/pkg/sentry/socket/hostinet/device.go index c5133f3bb..4267e3691 100644 --- a/pkg/sentry/socket/hostinet/device.go +++ b/pkg/sentry/socket/hostinet/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/hostinet/hostinet.go b/pkg/sentry/socket/hostinet/hostinet.go index 7858892ab..0d6f51d2b 100644 --- a/pkg/sentry/socket/hostinet/hostinet.go +++ b/pkg/sentry/socket/hostinet/hostinet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/hostinet/save_restore.go b/pkg/sentry/socket/hostinet/save_restore.go index 3827f082a..1dec33897 100644 --- a/pkg/sentry/socket/hostinet/save_restore.go +++ b/pkg/sentry/socket/hostinet/save_restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go index 49349074f..71884d3db 100644 --- a/pkg/sentry/socket/hostinet/socket.go +++ b/pkg/sentry/socket/hostinet/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/hostinet/socket_unsafe.go b/pkg/sentry/socket/hostinet/socket_unsafe.go index 59c8910ca..eed0c7837 100644 --- a/pkg/sentry/socket/hostinet/socket_unsafe.go +++ b/pkg/sentry/socket/hostinet/socket_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go index 4ce73c1f1..9c45991ba 100644 --- a/pkg/sentry/socket/hostinet/stack.go +++ b/pkg/sentry/socket/hostinet/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/netlink/message.go b/pkg/sentry/socket/netlink/message.go index a95172cba..5bd3b49ce 100644 --- a/pkg/sentry/socket/netlink/message.go +++ b/pkg/sentry/socket/netlink/message.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/netlink/port/port.go b/pkg/sentry/socket/netlink/port/port.go index 20b9a6e37..e9d3275b1 100644 --- a/pkg/sentry/socket/netlink/port/port.go +++ b/pkg/sentry/socket/netlink/port/port.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/netlink/port/port_test.go b/pkg/sentry/socket/netlink/port/port_test.go index 49b3b48ab..516f6cd6c 100644 --- a/pkg/sentry/socket/netlink/port/port_test.go +++ b/pkg/sentry/socket/netlink/port/port_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/netlink/provider.go b/pkg/sentry/socket/netlink/provider.go index 06786bd50..76cf12fd4 100644 --- a/pkg/sentry/socket/netlink/provider.go +++ b/pkg/sentry/socket/netlink/provider.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index e414b829b..9f0a81403 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go index a34f9d3ca..dc688eb00 100644 --- a/pkg/sentry/socket/netlink/socket.go +++ b/pkg/sentry/socket/netlink/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/rpcinet/conn/conn.go b/pkg/sentry/socket/rpcinet/conn/conn.go index 64106c4b5..f537c7f63 100644 --- a/pkg/sentry/socket/rpcinet/conn/conn.go +++ b/pkg/sentry/socket/rpcinet/conn/conn.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/rpcinet/device.go b/pkg/sentry/socket/rpcinet/device.go index d2b9f9222..44c0a39b7 100644 --- a/pkg/sentry/socket/rpcinet/device.go +++ b/pkg/sentry/socket/rpcinet/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/rpcinet/notifier/notifier.go b/pkg/sentry/socket/rpcinet/notifier/notifier.go index f06d12231..601e05994 100644 --- a/pkg/sentry/socket/rpcinet/notifier/notifier.go +++ b/pkg/sentry/socket/rpcinet/notifier/notifier.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/rpcinet/rpcinet.go b/pkg/sentry/socket/rpcinet/rpcinet.go index 6c98e6acb..5d4fd4dac 100644 --- a/pkg/sentry/socket/rpcinet/rpcinet.go +++ b/pkg/sentry/socket/rpcinet/rpcinet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/rpcinet/socket.go b/pkg/sentry/socket/rpcinet/socket.go index cf8f69efb..c028ed4dd 100644 --- a/pkg/sentry/socket/rpcinet/socket.go +++ b/pkg/sentry/socket/rpcinet/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/rpcinet/stack.go b/pkg/sentry/socket/rpcinet/stack.go index cb8344ec6..a1be711df 100644 --- a/pkg/sentry/socket/rpcinet/stack.go +++ b/pkg/sentry/socket/rpcinet/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/rpcinet/stack_unsafe.go b/pkg/sentry/socket/rpcinet/stack_unsafe.go index d04fb2069..e53f578ba 100644 --- a/pkg/sentry/socket/rpcinet/stack_unsafe.go +++ b/pkg/sentry/socket/rpcinet/stack_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/socket.go b/pkg/sentry/socket/socket.go index 62ba13782..7e840b452 100644 --- a/pkg/sentry/socket/socket.go +++ b/pkg/sentry/socket/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/device.go b/pkg/sentry/socket/unix/device.go index 41820dbb3..734d39ee6 100644 --- a/pkg/sentry/socket/unix/device.go +++ b/pkg/sentry/socket/unix/device.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/io.go b/pkg/sentry/socket/unix/io.go index 7d80e4393..382911d51 100644 --- a/pkg/sentry/socket/unix/io.go +++ b/pkg/sentry/socket/unix/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/transport/connectioned.go b/pkg/sentry/socket/unix/transport/connectioned.go index 62641bb34..18e492862 100644 --- a/pkg/sentry/socket/unix/transport/connectioned.go +++ b/pkg/sentry/socket/unix/transport/connectioned.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/transport/connectioned_state.go b/pkg/sentry/socket/unix/transport/connectioned_state.go index 608a6a97a..7e02a5db8 100644 --- a/pkg/sentry/socket/unix/transport/connectioned_state.go +++ b/pkg/sentry/socket/unix/transport/connectioned_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/transport/connectionless.go b/pkg/sentry/socket/unix/transport/connectionless.go index 728863f3f..43ff875e4 100644 --- a/pkg/sentry/socket/unix/transport/connectionless.go +++ b/pkg/sentry/socket/unix/transport/connectionless.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/transport/queue.go b/pkg/sentry/socket/unix/transport/queue.go index 45a58c600..b650caae7 100644 --- a/pkg/sentry/socket/unix/transport/queue.go +++ b/pkg/sentry/socket/unix/transport/queue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/transport/unix.go b/pkg/sentry/socket/unix/transport/unix.go index 12b1576bd..d5f7f7aa8 100644 --- a/pkg/sentry/socket/unix/transport/unix.go +++ b/pkg/sentry/socket/unix/transport/unix.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/socket/unix/unix.go b/pkg/sentry/socket/unix/unix.go index 01efd24d3..e9607aa01 100644 --- a/pkg/sentry/socket/unix/unix.go +++ b/pkg/sentry/socket/unix/unix.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/state/state.go b/pkg/sentry/state/state.go index 224f8b709..27fde505b 100644 --- a/pkg/sentry/state/state.go +++ b/pkg/sentry/state/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/state/state_metadata.go b/pkg/sentry/state/state_metadata.go index 7f047b808..b8e128c40 100644 --- a/pkg/sentry/state/state_metadata.go +++ b/pkg/sentry/state/state_metadata.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/state/state_unsafe.go b/pkg/sentry/state/state_unsafe.go index f02e12b2a..7745b6ac6 100644 --- a/pkg/sentry/state/state_unsafe.go +++ b/pkg/sentry/state/state_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/capability.go b/pkg/sentry/strace/capability.go index 9001181e7..f85d6636e 100644 --- a/pkg/sentry/strace/capability.go +++ b/pkg/sentry/strace/capability.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/sentry/strace/clone.go b/pkg/sentry/strace/clone.go index e18ce84dc..ff6a432c6 100644 --- a/pkg/sentry/strace/clone.go +++ b/pkg/sentry/strace/clone.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/futex.go b/pkg/sentry/strace/futex.go index f4aa7fcad..24301bda6 100644 --- a/pkg/sentry/strace/futex.go +++ b/pkg/sentry/strace/futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/linux64.go b/pkg/sentry/strace/linux64.go index 6043b8cb1..3650fd6e1 100644 --- a/pkg/sentry/strace/linux64.go +++ b/pkg/sentry/strace/linux64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/open.go b/pkg/sentry/strace/open.go index 3bf348d7a..140727b02 100644 --- a/pkg/sentry/strace/open.go +++ b/pkg/sentry/strace/open.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/poll.go b/pkg/sentry/strace/poll.go index b6b05423c..15605187d 100644 --- a/pkg/sentry/strace/poll.go +++ b/pkg/sentry/strace/poll.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/sentry/strace/ptrace.go b/pkg/sentry/strace/ptrace.go index 8c4b79227..485aacb8a 100644 --- a/pkg/sentry/strace/ptrace.go +++ b/pkg/sentry/strace/ptrace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/signal.go b/pkg/sentry/strace/signal.go index 524be0e15..f82460e1c 100644 --- a/pkg/sentry/strace/signal.go +++ b/pkg/sentry/strace/signal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/socket.go b/pkg/sentry/strace/socket.go index 4c1a9d469..dbe53b9a2 100644 --- a/pkg/sentry/strace/socket.go +++ b/pkg/sentry/strace/socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/strace.go b/pkg/sentry/strace/strace.go index 434a200d9..f4c1be4ce 100644 --- a/pkg/sentry/strace/strace.go +++ b/pkg/sentry/strace/strace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/strace.proto b/pkg/sentry/strace/strace.proto index f1fc539d6..4b2f73a5f 100644 --- a/pkg/sentry/strace/strace.proto +++ b/pkg/sentry/strace/strace.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/strace/syscalls.go b/pkg/sentry/strace/syscalls.go index 8c897fcbe..eae2d6c12 100644 --- a/pkg/sentry/strace/syscalls.go +++ b/pkg/sentry/strace/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/epoll.go b/pkg/sentry/syscalls/epoll.go index b90d191b7..ec1eab331 100644 --- a/pkg/sentry/syscalls/epoll.go +++ b/pkg/sentry/syscalls/epoll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/error.go b/pkg/sentry/syscalls/linux/error.go index 304a12dde..1ba3695fb 100644 --- a/pkg/sentry/syscalls/linux/error.go +++ b/pkg/sentry/syscalls/linux/error.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/flags.go b/pkg/sentry/syscalls/linux/flags.go index d2aec963a..d83e12971 100644 --- a/pkg/sentry/syscalls/linux/flags.go +++ b/pkg/sentry/syscalls/linux/flags.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index b9b4ccbd1..9a460ebdf 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sigset.go b/pkg/sentry/syscalls/linux/sigset.go index a033b7c70..5438b664b 100644 --- a/pkg/sentry/syscalls/linux/sigset.go +++ b/pkg/sentry/syscalls/linux/sigset.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_aio.go b/pkg/sentry/syscalls/linux/sys_aio.go index 61c2647bf..1b27b2415 100644 --- a/pkg/sentry/syscalls/linux/sys_aio.go +++ b/pkg/sentry/syscalls/linux/sys_aio.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_capability.go b/pkg/sentry/syscalls/linux/sys_capability.go index cf972dc28..622cb8d0d 100644 --- a/pkg/sentry/syscalls/linux/sys_capability.go +++ b/pkg/sentry/syscalls/linux/sys_capability.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_epoll.go b/pkg/sentry/syscalls/linux/sys_epoll.go index 200c46355..1467feb4e 100644 --- a/pkg/sentry/syscalls/linux/sys_epoll.go +++ b/pkg/sentry/syscalls/linux/sys_epoll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_eventfd.go b/pkg/sentry/syscalls/linux/sys_eventfd.go index 903172890..ca4ead488 100644 --- a/pkg/sentry/syscalls/linux/sys_eventfd.go +++ b/pkg/sentry/syscalls/linux/sys_eventfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_file.go b/pkg/sentry/syscalls/linux/sys_file.go index 967464c85..893322647 100644 --- a/pkg/sentry/syscalls/linux/sys_file.go +++ b/pkg/sentry/syscalls/linux/sys_file.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_futex.go b/pkg/sentry/syscalls/linux/sys_futex.go index f0c89cba4..7cef4b50c 100644 --- a/pkg/sentry/syscalls/linux/sys_futex.go +++ b/pkg/sentry/syscalls/linux/sys_futex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_getdents.go b/pkg/sentry/syscalls/linux/sys_getdents.go index 4b441b31b..1b597d5bc 100644 --- a/pkg/sentry/syscalls/linux/sys_getdents.go +++ b/pkg/sentry/syscalls/linux/sys_getdents.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_identity.go b/pkg/sentry/syscalls/linux/sys_identity.go index 8d594aa83..27e765a2d 100644 --- a/pkg/sentry/syscalls/linux/sys_identity.go +++ b/pkg/sentry/syscalls/linux/sys_identity.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_inotify.go b/pkg/sentry/syscalls/linux/sys_inotify.go index 26a505782..20269a769 100644 --- a/pkg/sentry/syscalls/linux/sys_inotify.go +++ b/pkg/sentry/syscalls/linux/sys_inotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_lseek.go b/pkg/sentry/syscalls/linux/sys_lseek.go index ad3bfd761..8aadc6d8c 100644 --- a/pkg/sentry/syscalls/linux/sys_lseek.go +++ b/pkg/sentry/syscalls/linux/sys_lseek.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_mmap.go b/pkg/sentry/syscalls/linux/sys_mmap.go index 805b251b1..64a6e639c 100644 --- a/pkg/sentry/syscalls/linux/sys_mmap.go +++ b/pkg/sentry/syscalls/linux/sys_mmap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_mount.go b/pkg/sentry/syscalls/linux/sys_mount.go index e110a553f..cf613bad0 100644 --- a/pkg/sentry/syscalls/linux/sys_mount.go +++ b/pkg/sentry/syscalls/linux/sys_mount.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_pipe.go b/pkg/sentry/syscalls/linux/sys_pipe.go index 3652c429e..036845c13 100644 --- a/pkg/sentry/syscalls/linux/sys_pipe.go +++ b/pkg/sentry/syscalls/linux/sys_pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_poll.go b/pkg/sentry/syscalls/linux/sys_poll.go index 17b6768e5..e32099dd4 100644 --- a/pkg/sentry/syscalls/linux/sys_poll.go +++ b/pkg/sentry/syscalls/linux/sys_poll.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_prctl.go b/pkg/sentry/syscalls/linux/sys_prctl.go index 7a29bd9b7..117ae1a0e 100644 --- a/pkg/sentry/syscalls/linux/sys_prctl.go +++ b/pkg/sentry/syscalls/linux/sys_prctl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_random.go b/pkg/sentry/syscalls/linux/sys_random.go index 452dff058..fc3959a7e 100644 --- a/pkg/sentry/syscalls/linux/sys_random.go +++ b/pkg/sentry/syscalls/linux/sys_random.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_read.go b/pkg/sentry/syscalls/linux/sys_read.go index 50c7d7a74..48b0fd49d 100644 --- a/pkg/sentry/syscalls/linux/sys_read.go +++ b/pkg/sentry/syscalls/linux/sys_read.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_rlimit.go b/pkg/sentry/syscalls/linux/sys_rlimit.go index 443334693..8b0379779 100644 --- a/pkg/sentry/syscalls/linux/sys_rlimit.go +++ b/pkg/sentry/syscalls/linux/sys_rlimit.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_rusage.go b/pkg/sentry/syscalls/linux/sys_rusage.go index ab07c77f9..003d718da 100644 --- a/pkg/sentry/syscalls/linux/sys_rusage.go +++ b/pkg/sentry/syscalls/linux/sys_rusage.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_sched.go b/pkg/sentry/syscalls/linux/sys_sched.go index e679a6694..8aea03abe 100644 --- a/pkg/sentry/syscalls/linux/sys_sched.go +++ b/pkg/sentry/syscalls/linux/sys_sched.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_seccomp.go b/pkg/sentry/syscalls/linux/sys_seccomp.go index f08fdf5cb..b4262162a 100644 --- a/pkg/sentry/syscalls/linux/sys_seccomp.go +++ b/pkg/sentry/syscalls/linux/sys_seccomp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go index 86f850ef1..5bd61ab87 100644 --- a/pkg/sentry/syscalls/linux/sys_sem.go +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_shm.go b/pkg/sentry/syscalls/linux/sys_shm.go index a0d3a73c5..d0eceac7c 100644 --- a/pkg/sentry/syscalls/linux/sys_shm.go +++ b/pkg/sentry/syscalls/linux/sys_shm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_signal.go b/pkg/sentry/syscalls/linux/sys_signal.go index a539354c5..7fbeb4fcd 100644 --- a/pkg/sentry/syscalls/linux/sys_signal.go +++ b/pkg/sentry/syscalls/linux/sys_signal.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_socket.go b/pkg/sentry/syscalls/linux/sys_socket.go index c8748958a..69862f110 100644 --- a/pkg/sentry/syscalls/linux/sys_socket.go +++ b/pkg/sentry/syscalls/linux/sys_socket.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_stat.go b/pkg/sentry/syscalls/linux/sys_stat.go index 49c225011..10fc201ef 100644 --- a/pkg/sentry/syscalls/linux/sys_stat.go +++ b/pkg/sentry/syscalls/linux/sys_stat.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_sync.go b/pkg/sentry/syscalls/linux/sys_sync.go index 68488330f..4352482fb 100644 --- a/pkg/sentry/syscalls/linux/sys_sync.go +++ b/pkg/sentry/syscalls/linux/sys_sync.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_sysinfo.go b/pkg/sentry/syscalls/linux/sys_sysinfo.go index 6f7acf98f..ecf88edc1 100644 --- a/pkg/sentry/syscalls/linux/sys_sysinfo.go +++ b/pkg/sentry/syscalls/linux/sys_sysinfo.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_syslog.go b/pkg/sentry/syscalls/linux/sys_syslog.go index 7193b7aed..9efc58d34 100644 --- a/pkg/sentry/syscalls/linux/sys_syslog.go +++ b/pkg/sentry/syscalls/linux/sys_syslog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go index ddcb5b789..23c2f7035 100644 --- a/pkg/sentry/syscalls/linux/sys_thread.go +++ b/pkg/sentry/syscalls/linux/sys_thread.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_time.go b/pkg/sentry/syscalls/linux/sys_time.go index 063fbb106..b4f2609c0 100644 --- a/pkg/sentry/syscalls/linux/sys_time.go +++ b/pkg/sentry/syscalls/linux/sys_time.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_timer.go b/pkg/sentry/syscalls/linux/sys_timer.go index 6baf4599b..04ea7a4e9 100644 --- a/pkg/sentry/syscalls/linux/sys_timer.go +++ b/pkg/sentry/syscalls/linux/sys_timer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_timerfd.go b/pkg/sentry/syscalls/linux/sys_timerfd.go index f70d13682..ec0155cbb 100644 --- a/pkg/sentry/syscalls/linux/sys_timerfd.go +++ b/pkg/sentry/syscalls/linux/sys_timerfd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_tls.go b/pkg/sentry/syscalls/linux/sys_tls.go index 8ea78093b..1e8312e00 100644 --- a/pkg/sentry/syscalls/linux/sys_tls.go +++ b/pkg/sentry/syscalls/linux/sys_tls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_utsname.go b/pkg/sentry/syscalls/linux/sys_utsname.go index f7545b965..fa81fe10e 100644 --- a/pkg/sentry/syscalls/linux/sys_utsname.go +++ b/pkg/sentry/syscalls/linux/sys_utsname.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/sys_write.go b/pkg/sentry/syscalls/linux/sys_write.go index e405608c4..1da72d606 100644 --- a/pkg/sentry/syscalls/linux/sys_write.go +++ b/pkg/sentry/syscalls/linux/sys_write.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/linux/timespec.go b/pkg/sentry/syscalls/linux/timespec.go index 752ec326d..fa6fcdc0b 100644 --- a/pkg/sentry/syscalls/linux/timespec.go +++ b/pkg/sentry/syscalls/linux/timespec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go index 425ce900c..5d10b3824 100644 --- a/pkg/sentry/syscalls/syscalls.go +++ b/pkg/sentry/syscalls/syscalls.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/calibrated_clock.go b/pkg/sentry/time/calibrated_clock.go index a98bcd7de..c27e391c9 100644 --- a/pkg/sentry/time/calibrated_clock.go +++ b/pkg/sentry/time/calibrated_clock.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/calibrated_clock_test.go b/pkg/sentry/time/calibrated_clock_test.go index a9237630e..d6622bfe2 100644 --- a/pkg/sentry/time/calibrated_clock_test.go +++ b/pkg/sentry/time/calibrated_clock_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/clock_id.go b/pkg/sentry/time/clock_id.go index 1317a5dad..724f59dd9 100644 --- a/pkg/sentry/time/clock_id.go +++ b/pkg/sentry/time/clock_id.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/clocks.go b/pkg/sentry/time/clocks.go index e26386520..837e86094 100644 --- a/pkg/sentry/time/clocks.go +++ b/pkg/sentry/time/clocks.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/muldiv_amd64.s b/pkg/sentry/time/muldiv_amd64.s index bfcb8c724..028c6684e 100644 --- a/pkg/sentry/time/muldiv_amd64.s +++ b/pkg/sentry/time/muldiv_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/muldiv_arm64.s b/pkg/sentry/time/muldiv_arm64.s index 5fa82a136..5ad57a8a3 100644 --- a/pkg/sentry/time/muldiv_arm64.s +++ b/pkg/sentry/time/muldiv_arm64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/parameters.go b/pkg/sentry/time/parameters.go index 8568b1193..63cf7c4a3 100644 --- a/pkg/sentry/time/parameters.go +++ b/pkg/sentry/time/parameters.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/parameters_test.go b/pkg/sentry/time/parameters_test.go index 4a0c4e880..e1b9084ac 100644 --- a/pkg/sentry/time/parameters_test.go +++ b/pkg/sentry/time/parameters_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/sampler.go b/pkg/sentry/time/sampler.go index 445690d49..2140a99b7 100644 --- a/pkg/sentry/time/sampler.go +++ b/pkg/sentry/time/sampler.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/sampler_test.go b/pkg/sentry/time/sampler_test.go index ec0e442b6..3e70a1134 100644 --- a/pkg/sentry/time/sampler_test.go +++ b/pkg/sentry/time/sampler_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/sampler_unsafe.go b/pkg/sentry/time/sampler_unsafe.go index 0f8eb4fc8..e76180217 100644 --- a/pkg/sentry/time/sampler_unsafe.go +++ b/pkg/sentry/time/sampler_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/tsc_amd64.s b/pkg/sentry/time/tsc_amd64.s index e53d477f7..6a8eed664 100644 --- a/pkg/sentry/time/tsc_amd64.s +++ b/pkg/sentry/time/tsc_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/time/tsc_arm64.s b/pkg/sentry/time/tsc_arm64.s index c1c9760ef..da9fa4112 100644 --- a/pkg/sentry/time/tsc_arm64.s +++ b/pkg/sentry/time/tsc_arm64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/unimpl/events.go b/pkg/sentry/unimpl/events.go index f78f8c981..d92766e2d 100644 --- a/pkg/sentry/unimpl/events.go +++ b/pkg/sentry/unimpl/events.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/unimpl/unimplemented_syscall.proto b/pkg/sentry/unimpl/unimplemented_syscall.proto index 41579b016..0d7a94be7 100644 --- a/pkg/sentry/unimpl/unimplemented_syscall.proto +++ b/pkg/sentry/unimpl/unimplemented_syscall.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/uniqueid/context.go b/pkg/sentry/uniqueid/context.go index 399d98c29..e55b89689 100644 --- a/pkg/sentry/uniqueid/context.go +++ b/pkg/sentry/uniqueid/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usage/cpu.go b/pkg/sentry/usage/cpu.go index cbd7cfe19..bfc282d69 100644 --- a/pkg/sentry/usage/cpu.go +++ b/pkg/sentry/usage/cpu.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usage/io.go b/pkg/sentry/usage/io.go index 8e27a0a88..dfcd3a49d 100644 --- a/pkg/sentry/usage/io.go +++ b/pkg/sentry/usage/io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usage/memory.go b/pkg/sentry/usage/memory.go index 5be9ed9c6..c316f1597 100644 --- a/pkg/sentry/usage/memory.go +++ b/pkg/sentry/usage/memory.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usage/memory_unsafe.go b/pkg/sentry/usage/memory_unsafe.go index a3ae668a5..9e0014ca0 100644 --- a/pkg/sentry/usage/memory_unsafe.go +++ b/pkg/sentry/usage/memory_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usage/usage.go b/pkg/sentry/usage/usage.go index ab327f8e2..e3d33a965 100644 --- a/pkg/sentry/usage/usage.go +++ b/pkg/sentry/usage/usage.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/access_type.go b/pkg/sentry/usermem/access_type.go index 9e6a27bcf..9c1742a59 100644 --- a/pkg/sentry/usermem/access_type.go +++ b/pkg/sentry/usermem/access_type.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/addr.go b/pkg/sentry/usermem/addr.go index 2a75aa60c..e79210804 100644 --- a/pkg/sentry/usermem/addr.go +++ b/pkg/sentry/usermem/addr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/addr_range_seq_test.go b/pkg/sentry/usermem/addr_range_seq_test.go index bd6a1ec8a..82f735026 100644 --- a/pkg/sentry/usermem/addr_range_seq_test.go +++ b/pkg/sentry/usermem/addr_range_seq_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/addr_range_seq_unsafe.go b/pkg/sentry/usermem/addr_range_seq_unsafe.go index f5fd446fa..c09337c15 100644 --- a/pkg/sentry/usermem/addr_range_seq_unsafe.go +++ b/pkg/sentry/usermem/addr_range_seq_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/bytes_io.go b/pkg/sentry/usermem/bytes_io.go index 274f568d0..f98d82168 100644 --- a/pkg/sentry/usermem/bytes_io.go +++ b/pkg/sentry/usermem/bytes_io.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/bytes_io_unsafe.go b/pkg/sentry/usermem/bytes_io_unsafe.go index 7add8bc82..bb49d2ff3 100644 --- a/pkg/sentry/usermem/bytes_io_unsafe.go +++ b/pkg/sentry/usermem/bytes_io_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/usermem.go b/pkg/sentry/usermem/usermem.go index 4c7d5014a..31e4d6ada 100644 --- a/pkg/sentry/usermem/usermem.go +++ b/pkg/sentry/usermem/usermem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/usermem_arm64.go b/pkg/sentry/usermem/usermem_arm64.go index 7fd4ce963..fdfc30a66 100644 --- a/pkg/sentry/usermem/usermem_arm64.go +++ b/pkg/sentry/usermem/usermem_arm64.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/sentry/usermem/usermem_test.go b/pkg/sentry/usermem/usermem_test.go index 1991a9641..4a07118b7 100644 --- a/pkg/sentry/usermem/usermem_test.go +++ b/pkg/sentry/usermem/usermem_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/usermem/usermem_unsafe.go b/pkg/sentry/usermem/usermem_unsafe.go index 3895e7871..876783e78 100644 --- a/pkg/sentry/usermem/usermem_unsafe.go +++ b/pkg/sentry/usermem/usermem_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/sentry/usermem/usermem_x86.go b/pkg/sentry/usermem/usermem_x86.go index 9ec90f9ff..8059b72d2 100644 --- a/pkg/sentry/usermem/usermem_x86.go +++ b/pkg/sentry/usermem/usermem_x86.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sentry/watchdog/watchdog.go b/pkg/sentry/watchdog/watchdog.go index b4f1e3a4f..2fc4472dd 100644 --- a/pkg/sentry/watchdog/watchdog.go +++ b/pkg/sentry/watchdog/watchdog.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sleep/commit_amd64.s b/pkg/sleep/commit_amd64.s index d08df7f37..bc4ac2c3c 100644 --- a/pkg/sleep/commit_amd64.s +++ b/pkg/sleep/commit_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sleep/commit_asm.go b/pkg/sleep/commit_asm.go index 90eef4cbc..35e2cc337 100644 --- a/pkg/sleep/commit_asm.go +++ b/pkg/sleep/commit_asm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sleep/commit_noasm.go b/pkg/sleep/commit_noasm.go index 967d22e24..686b1da3d 100644 --- a/pkg/sleep/commit_noasm.go +++ b/pkg/sleep/commit_noasm.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sleep/empty.s b/pkg/sleep/empty.s index 85d52cd9c..fb37360ac 100644 --- a/pkg/sleep/empty.s +++ b/pkg/sleep/empty.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sleep/sleep_test.go b/pkg/sleep/sleep_test.go index 8feb9ffc2..130806c86 100644 --- a/pkg/sleep/sleep_test.go +++ b/pkg/sleep/sleep_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/sleep/sleep_unsafe.go b/pkg/sleep/sleep_unsafe.go index 45fb6f0ea..62e0abc34 100644 --- a/pkg/sleep/sleep_unsafe.go +++ b/pkg/sleep/sleep_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/decode.go b/pkg/state/decode.go index 54b5ad8b8..73a59f871 100644 --- a/pkg/state/decode.go +++ b/pkg/state/decode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/encode.go b/pkg/state/encode.go index fe8512bbf..b0714170b 100644 --- a/pkg/state/encode.go +++ b/pkg/state/encode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/encode_unsafe.go b/pkg/state/encode_unsafe.go index be94742a8..457e6dbb7 100644 --- a/pkg/state/encode_unsafe.go +++ b/pkg/state/encode_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/map.go b/pkg/state/map.go index 0035d7250..1fb9b47b8 100644 --- a/pkg/state/map.go +++ b/pkg/state/map.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/object.proto b/pkg/state/object.proto index d3b46ea97..952289069 100644 --- a/pkg/state/object.proto +++ b/pkg/state/object.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/printer.go b/pkg/state/printer.go index aee4b69fb..5174c3ba3 100644 --- a/pkg/state/printer.go +++ b/pkg/state/printer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/state.go b/pkg/state/state.go index 4486f83a7..cf7df803a 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index 22bcad9e1..7c24bbcda 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/statefile/statefile.go b/pkg/state/statefile/statefile.go index c21e3bb0e..ad4e3b43e 100644 --- a/pkg/state/statefile/statefile.go +++ b/pkg/state/statefile/statefile.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/statefile/statefile_test.go b/pkg/state/statefile/statefile_test.go index b4f400e01..60b769895 100644 --- a/pkg/state/statefile/statefile_test.go +++ b/pkg/state/statefile/statefile_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/state/stats.go b/pkg/state/stats.go index 17ca258fc..eb51cda47 100644 --- a/pkg/state/stats.go +++ b/pkg/state/stats.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/syserr/host_linux.go b/pkg/syserr/host_linux.go index 74bbe9f5b..fc6ef60a1 100644 --- a/pkg/syserr/host_linux.go +++ b/pkg/syserr/host_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/syserr/netstack.go b/pkg/syserr/netstack.go index 1a23919ef..bd489b424 100644 --- a/pkg/syserr/netstack.go +++ b/pkg/syserr/netstack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/syserr/syserr.go b/pkg/syserr/syserr.go index 232634dd4..4ddbd3322 100644 --- a/pkg/syserr/syserr.go +++ b/pkg/syserr/syserr.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/syserror/syserror.go b/pkg/syserror/syserror.go index 5558cccff..345653544 100644 --- a/pkg/syserror/syserror.go +++ b/pkg/syserror/syserror.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/syserror/syserror_test.go b/pkg/syserror/syserror_test.go index 0f0da5781..f2a10ee7b 100644 --- a/pkg/syserror/syserror_test.go +++ b/pkg/syserror/syserror_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/adapters/gonet/gonet.go b/pkg/tcpip/adapters/gonet/gonet.go index 628e28f57..df8bf435d 100644 --- a/pkg/tcpip/adapters/gonet/gonet.go +++ b/pkg/tcpip/adapters/gonet/gonet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/adapters/gonet/gonet_test.go b/pkg/tcpip/adapters/gonet/gonet_test.go index e84f73feb..2c81c5697 100644 --- a/pkg/tcpip/adapters/gonet/gonet_test.go +++ b/pkg/tcpip/adapters/gonet/gonet_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/buffer/prependable.go b/pkg/tcpip/buffer/prependable.go index d3a9a0f88..43cbbc74c 100644 --- a/pkg/tcpip/buffer/prependable.go +++ b/pkg/tcpip/buffer/prependable.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/buffer/view.go b/pkg/tcpip/buffer/view.go index 43cbb9461..1a9d40778 100644 --- a/pkg/tcpip/buffer/view.go +++ b/pkg/tcpip/buffer/view.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/buffer/view_test.go b/pkg/tcpip/buffer/view_test.go index 74a0a96fc..ebc3a17b7 100644 --- a/pkg/tcpip/buffer/view_test.go +++ b/pkg/tcpip/buffer/view_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/checker/checker.go b/pkg/tcpip/checker/checker.go index 5dfb3ca1d..6e7edf3ab 100644 --- a/pkg/tcpip/checker/checker.go +++ b/pkg/tcpip/checker/checker.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/hash/jenkins/jenkins.go b/pkg/tcpip/hash/jenkins/jenkins.go index e66d5f12b..52c22230e 100644 --- a/pkg/tcpip/hash/jenkins/jenkins.go +++ b/pkg/tcpip/hash/jenkins/jenkins.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/hash/jenkins/jenkins_test.go b/pkg/tcpip/hash/jenkins/jenkins_test.go index 9d86174aa..4c78b5808 100644 --- a/pkg/tcpip/hash/jenkins/jenkins_test.go +++ b/pkg/tcpip/hash/jenkins/jenkins_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/arp.go b/pkg/tcpip/header/arp.go index 22b259ccb..55fe7292c 100644 --- a/pkg/tcpip/header/arp.go +++ b/pkg/tcpip/header/arp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/checksum.go b/pkg/tcpip/header/checksum.go index 2e8c65fac..2eaa7938a 100644 --- a/pkg/tcpip/header/checksum.go +++ b/pkg/tcpip/header/checksum.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/eth.go b/pkg/tcpip/header/eth.go index 77365bc41..76143f454 100644 --- a/pkg/tcpip/header/eth.go +++ b/pkg/tcpip/header/eth.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/gue.go b/pkg/tcpip/header/gue.go index 2ad13955a..10d358c0e 100644 --- a/pkg/tcpip/header/gue.go +++ b/pkg/tcpip/header/gue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/icmpv4.go b/pkg/tcpip/header/icmpv4.go index 3ac89cdae..782e1053c 100644 --- a/pkg/tcpip/header/icmpv4.go +++ b/pkg/tcpip/header/icmpv4.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/icmpv6.go b/pkg/tcpip/header/icmpv6.go index e317975e8..d0b10d849 100644 --- a/pkg/tcpip/header/icmpv6.go +++ b/pkg/tcpip/header/icmpv6.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/interfaces.go b/pkg/tcpip/header/interfaces.go index ac327d8a5..fb250ea30 100644 --- a/pkg/tcpip/header/interfaces.go +++ b/pkg/tcpip/header/interfaces.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/ipv4.go b/pkg/tcpip/header/ipv4.go index c3b8fb00e..96e461491 100644 --- a/pkg/tcpip/header/ipv4.go +++ b/pkg/tcpip/header/ipv4.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/ipv6.go b/pkg/tcpip/header/ipv6.go index 3d24736c7..66820a466 100644 --- a/pkg/tcpip/header/ipv6.go +++ b/pkg/tcpip/header/ipv6.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/ipv6_fragment.go b/pkg/tcpip/header/ipv6_fragment.go index e36d5177b..6d896355a 100644 --- a/pkg/tcpip/header/ipv6_fragment.go +++ b/pkg/tcpip/header/ipv6_fragment.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/ipversion_test.go b/pkg/tcpip/header/ipversion_test.go index 8301ba5cf..0c830180e 100644 --- a/pkg/tcpip/header/ipversion_test.go +++ b/pkg/tcpip/header/ipversion_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/tcp.go b/pkg/tcpip/header/tcp.go index e656ebb15..0cd89b992 100644 --- a/pkg/tcpip/header/tcp.go +++ b/pkg/tcpip/header/tcp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/tcp_test.go b/pkg/tcpip/header/tcp_test.go index 7cd98df3b..9a2b99489 100644 --- a/pkg/tcpip/header/tcp_test.go +++ b/pkg/tcpip/header/tcp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/header/udp.go b/pkg/tcpip/header/udp.go index e8c860436..2205fec18 100644 --- a/pkg/tcpip/header/udp.go +++ b/pkg/tcpip/header/udp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go index f7501a1bc..ee9dd8700 100644 --- a/pkg/tcpip/link/channel/channel.go +++ b/pkg/tcpip/link/channel/channel.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/fdbased/endpoint.go b/pkg/tcpip/link/fdbased/endpoint.go index 8f4d67074..4da376774 100644 --- a/pkg/tcpip/link/fdbased/endpoint.go +++ b/pkg/tcpip/link/fdbased/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/fdbased/endpoint_test.go b/pkg/tcpip/link/fdbased/endpoint_test.go index c8b037d57..31138e4ac 100644 --- a/pkg/tcpip/link/fdbased/endpoint_test.go +++ b/pkg/tcpip/link/fdbased/endpoint_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/fdbased/endpoint_unsafe.go b/pkg/tcpip/link/fdbased/endpoint_unsafe.go index 36e7fe5a9..97a477b61 100644 --- a/pkg/tcpip/link/fdbased/endpoint_unsafe.go +++ b/pkg/tcpip/link/fdbased/endpoint_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/tcpip/link/fdbased/mmap.go b/pkg/tcpip/link/fdbased/mmap.go index f1e71c233..430c85a42 100644 --- a/pkg/tcpip/link/fdbased/mmap.go +++ b/pkg/tcpip/link/fdbased/mmap.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/tcpip/link/fdbased/mmap_amd64_unsafe.go b/pkg/tcpip/link/fdbased/mmap_amd64_unsafe.go index e5ac7996d..135da2498 100644 --- a/pkg/tcpip/link/fdbased/mmap_amd64_unsafe.go +++ b/pkg/tcpip/link/fdbased/mmap_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/tcpip/link/loopback/loopback.go b/pkg/tcpip/link/loopback/loopback.go index 2dc4bcfda..2c1148123 100644 --- a/pkg/tcpip/link/loopback/loopback.go +++ b/pkg/tcpip/link/loopback/loopback.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/muxed/injectable.go b/pkg/tcpip/link/muxed/injectable.go index b3e71c7fc..be07b7c29 100644 --- a/pkg/tcpip/link/muxed/injectable.go +++ b/pkg/tcpip/link/muxed/injectable.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/tcpip/link/muxed/injectable_test.go b/pkg/tcpip/link/muxed/injectable_test.go index 031449a05..5d40dfacc 100644 --- a/pkg/tcpip/link/muxed/injectable_test.go +++ b/pkg/tcpip/link/muxed/injectable_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/tcpip/link/rawfile/blockingpoll_amd64.s b/pkg/tcpip/link/rawfile/blockingpoll_amd64.s index 9dade5421..b54131573 100644 --- a/pkg/tcpip/link/rawfile/blockingpoll_amd64.s +++ b/pkg/tcpip/link/rawfile/blockingpoll_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go b/pkg/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go index 3ba96a123..0b51982c6 100644 --- a/pkg/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go +++ b/pkg/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go b/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go index 94ddad8ea..4eab77c74 100644 --- a/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go +++ b/pkg/tcpip/link/rawfile/blockingpoll_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/rawfile/errors.go b/pkg/tcpip/link/rawfile/errors.go index 7359849b1..8bde41637 100644 --- a/pkg/tcpip/link/rawfile/errors.go +++ b/pkg/tcpip/link/rawfile/errors.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/rawfile/rawfile_unsafe.go b/pkg/tcpip/link/rawfile/rawfile_unsafe.go index fe2779125..86db7a487 100644 --- a/pkg/tcpip/link/rawfile/rawfile_unsafe.go +++ b/pkg/tcpip/link/rawfile/rawfile_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/pipe/pipe.go b/pkg/tcpip/link/sharedmem/pipe/pipe.go index e014324cc..74c9f0311 100644 --- a/pkg/tcpip/link/sharedmem/pipe/pipe.go +++ b/pkg/tcpip/link/sharedmem/pipe/pipe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/pipe/pipe_test.go b/pkg/tcpip/link/sharedmem/pipe/pipe_test.go index 30742ccb1..59ef69a8b 100644 --- a/pkg/tcpip/link/sharedmem/pipe/pipe_test.go +++ b/pkg/tcpip/link/sharedmem/pipe/pipe_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go b/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go index f491d74a2..62d17029e 100644 --- a/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go +++ b/pkg/tcpip/link/sharedmem/pipe/pipe_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/pipe/rx.go b/pkg/tcpip/link/sharedmem/pipe/rx.go index 8d641c76f..f22e533ac 100644 --- a/pkg/tcpip/link/sharedmem/pipe/rx.go +++ b/pkg/tcpip/link/sharedmem/pipe/rx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/pipe/tx.go b/pkg/tcpip/link/sharedmem/pipe/tx.go index e75175d98..9841eb231 100644 --- a/pkg/tcpip/link/sharedmem/pipe/tx.go +++ b/pkg/tcpip/link/sharedmem/pipe/tx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/queue/queue_test.go b/pkg/tcpip/link/sharedmem/queue/queue_test.go index 391165bc3..d3f8f4b8b 100644 --- a/pkg/tcpip/link/sharedmem/queue/queue_test.go +++ b/pkg/tcpip/link/sharedmem/queue/queue_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/queue/rx.go b/pkg/tcpip/link/sharedmem/queue/rx.go index d3a5da08a..d9aecf2d9 100644 --- a/pkg/tcpip/link/sharedmem/queue/rx.go +++ b/pkg/tcpip/link/sharedmem/queue/rx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/queue/tx.go b/pkg/tcpip/link/sharedmem/queue/tx.go index 845108db1..a24dccd11 100644 --- a/pkg/tcpip/link/sharedmem/queue/tx.go +++ b/pkg/tcpip/link/sharedmem/queue/tx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/rx.go b/pkg/tcpip/link/sharedmem/rx.go index 3eeab769e..215cb607f 100644 --- a/pkg/tcpip/link/sharedmem/rx.go +++ b/pkg/tcpip/link/sharedmem/rx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/sharedmem.go b/pkg/tcpip/link/sharedmem/sharedmem.go index 6e6aa5a13..e34b780f8 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem.go +++ b/pkg/tcpip/link/sharedmem/sharedmem.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/sharedmem_test.go b/pkg/tcpip/link/sharedmem/sharedmem_test.go index 1f44e224c..65b9d7085 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_test.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go b/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go index b91adbaf7..f7e816a41 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sharedmem/tx.go b/pkg/tcpip/link/sharedmem/tx.go index 37da34831..ac3577aa6 100644 --- a/pkg/tcpip/link/sharedmem/tx.go +++ b/pkg/tcpip/link/sharedmem/tx.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sniffer/pcap.go b/pkg/tcpip/link/sniffer/pcap.go index 3d0d8d852..c16c19647 100644 --- a/pkg/tcpip/link/sniffer/pcap.go +++ b/pkg/tcpip/link/sniffer/pcap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/sniffer/sniffer.go b/pkg/tcpip/link/sniffer/sniffer.go index 462a6e3a3..e87ae07d7 100644 --- a/pkg/tcpip/link/sniffer/sniffer.go +++ b/pkg/tcpip/link/sniffer/sniffer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/tun/tun_unsafe.go b/pkg/tcpip/link/tun/tun_unsafe.go index e4c589dda..09ca9b527 100644 --- a/pkg/tcpip/link/tun/tun_unsafe.go +++ b/pkg/tcpip/link/tun/tun_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/waitable/waitable.go b/pkg/tcpip/link/waitable/waitable.go index bd9f9845b..21690a226 100644 --- a/pkg/tcpip/link/waitable/waitable.go +++ b/pkg/tcpip/link/waitable/waitable.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/link/waitable/waitable_test.go b/pkg/tcpip/link/waitable/waitable_test.go index a2df6be95..62054fb7f 100644 --- a/pkg/tcpip/link/waitable/waitable_test.go +++ b/pkg/tcpip/link/waitable/waitable_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/arp/arp.go b/pkg/tcpip/network/arp/arp.go index 975919e80..a3f2bce3e 100644 --- a/pkg/tcpip/network/arp/arp.go +++ b/pkg/tcpip/network/arp/arp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/arp/arp_test.go b/pkg/tcpip/network/arp/arp_test.go index 14b9cb8b6..1b971b1a3 100644 --- a/pkg/tcpip/network/arp/arp_test.go +++ b/pkg/tcpip/network/arp/arp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/fragmentation/frag_heap.go b/pkg/tcpip/network/fragmentation/frag_heap.go index 55615c8e6..9ad3e5a8a 100644 --- a/pkg/tcpip/network/fragmentation/frag_heap.go +++ b/pkg/tcpip/network/fragmentation/frag_heap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/fragmentation/frag_heap_test.go b/pkg/tcpip/network/fragmentation/frag_heap_test.go index 1b1b72e88..3a2486ba8 100644 --- a/pkg/tcpip/network/fragmentation/frag_heap_test.go +++ b/pkg/tcpip/network/fragmentation/frag_heap_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/fragmentation/fragmentation.go b/pkg/tcpip/network/fragmentation/fragmentation.go index a5dda0398..e90edb375 100644 --- a/pkg/tcpip/network/fragmentation/fragmentation.go +++ b/pkg/tcpip/network/fragmentation/fragmentation.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/fragmentation/fragmentation_test.go b/pkg/tcpip/network/fragmentation/fragmentation_test.go index 5bf3463a9..99ded68a3 100644 --- a/pkg/tcpip/network/fragmentation/fragmentation_test.go +++ b/pkg/tcpip/network/fragmentation/fragmentation_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/fragmentation/reassembler.go b/pkg/tcpip/network/fragmentation/reassembler.go index c9ad2bef6..04f9ab964 100644 --- a/pkg/tcpip/network/fragmentation/reassembler.go +++ b/pkg/tcpip/network/fragmentation/reassembler.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/fragmentation/reassembler_test.go b/pkg/tcpip/network/fragmentation/reassembler_test.go index a2bc9707a..7eee0710d 100644 --- a/pkg/tcpip/network/fragmentation/reassembler_test.go +++ b/pkg/tcpip/network/fragmentation/reassembler_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/hash/hash.go b/pkg/tcpip/network/hash/hash.go index 07960ddf0..0c91905dc 100644 --- a/pkg/tcpip/network/hash/hash.go +++ b/pkg/tcpip/network/hash/hash.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/ip_test.go b/pkg/tcpip/network/ip_test.go index 522009fac..4b822e2c6 100644 --- a/pkg/tcpip/network/ip_test.go +++ b/pkg/tcpip/network/ip_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/ipv4/icmp.go b/pkg/tcpip/network/ipv4/icmp.go index 1c3acda4b..9cb81245a 100644 --- a/pkg/tcpip/network/ipv4/icmp.go +++ b/pkg/tcpip/network/ipv4/icmp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index cbdca98a5..c6af0db79 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/ipv4/ipv4_test.go b/pkg/tcpip/network/ipv4/ipv4_test.go index 42e85564e..146143ab3 100644 --- a/pkg/tcpip/network/ipv4/ipv4_test.go +++ b/pkg/tcpip/network/ipv4/ipv4_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go index be28be36d..9c011e107 100644 --- a/pkg/tcpip/network/ipv6/icmp.go +++ b/pkg/tcpip/network/ipv6/icmp.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/ipv6/icmp_test.go b/pkg/tcpip/network/ipv6/icmp_test.go index 8b57a0641..d8737a616 100644 --- a/pkg/tcpip/network/ipv6/icmp_test.go +++ b/pkg/tcpip/network/ipv6/icmp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go index 9a743ea80..4b8cd496b 100644 --- a/pkg/tcpip/network/ipv6/ipv6.go +++ b/pkg/tcpip/network/ipv6/ipv6.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/ports/ports.go b/pkg/tcpip/ports/ports.go index d212a5792..a1712b590 100644 --- a/pkg/tcpip/ports/ports.go +++ b/pkg/tcpip/ports/ports.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/ports/ports_test.go b/pkg/tcpip/ports/ports_test.go index 01e7320b4..8466c661b 100644 --- a/pkg/tcpip/ports/ports_test.go +++ b/pkg/tcpip/ports/ports_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/sample/tun_tcp_connect/main.go b/pkg/tcpip/sample/tun_tcp_connect/main.go index cf8900c4d..1681de56e 100644 --- a/pkg/tcpip/sample/tun_tcp_connect/main.go +++ b/pkg/tcpip/sample/tun_tcp_connect/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/sample/tun_tcp_echo/main.go b/pkg/tcpip/sample/tun_tcp_echo/main.go index da6202f97..642607f83 100644 --- a/pkg/tcpip/sample/tun_tcp_echo/main.go +++ b/pkg/tcpip/sample/tun_tcp_echo/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/seqnum/seqnum.go b/pkg/tcpip/seqnum/seqnum.go index f2b988839..b40a3c212 100644 --- a/pkg/tcpip/seqnum/seqnum.go +++ b/pkg/tcpip/seqnum/seqnum.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/linkaddrcache.go b/pkg/tcpip/stack/linkaddrcache.go index 40e4bdb4a..42b9768ae 100644 --- a/pkg/tcpip/stack/linkaddrcache.go +++ b/pkg/tcpip/stack/linkaddrcache.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/linkaddrcache_test.go b/pkg/tcpip/stack/linkaddrcache_test.go index 77a09ca86..91b2ffea8 100644 --- a/pkg/tcpip/stack/linkaddrcache_test.go +++ b/pkg/tcpip/stack/linkaddrcache_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index c18571b0f..8008d9870 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index 6e1660051..c70533a35 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/route.go b/pkg/tcpip/stack/route.go index 8ae562dcd..3d4c282a9 100644 --- a/pkg/tcpip/stack/route.go +++ b/pkg/tcpip/stack/route.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index cb9ffe9c2..f204ca790 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/stack_global_state.go b/pkg/tcpip/stack/stack_global_state.go index 3d7e4b719..dfec4258a 100644 --- a/pkg/tcpip/stack/stack_global_state.go +++ b/pkg/tcpip/stack/stack_global_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/stack_test.go b/pkg/tcpip/stack/stack_test.go index b5375df3c..351f63221 100644 --- a/pkg/tcpip/stack/stack_test.go +++ b/pkg/tcpip/stack/stack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/transport_demuxer.go b/pkg/tcpip/stack/transport_demuxer.go index a8ac18e72..e8b562ad9 100644 --- a/pkg/tcpip/stack/transport_demuxer.go +++ b/pkg/tcpip/stack/transport_demuxer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/stack/transport_test.go b/pkg/tcpip/stack/transport_test.go index 2df974bf2..8d74f1543 100644 --- a/pkg/tcpip/stack/transport_test.go +++ b/pkg/tcpip/stack/transport_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index b09137f08..9367c8c02 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/tcpip_test.go b/pkg/tcpip/tcpip_test.go index 1f7b04398..ebb1c1b56 100644 --- a/pkg/tcpip/tcpip_test.go +++ b/pkg/tcpip/tcpip_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/time.s b/pkg/tcpip/time.s index 85d52cd9c..fb37360ac 100644 --- a/pkg/tcpip/time.s +++ b/pkg/tcpip/time.s @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/time_unsafe.go b/pkg/tcpip/time_unsafe.go index 7ec5741af..1a307483b 100644 --- a/pkg/tcpip/time_unsafe.go +++ b/pkg/tcpip/time_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/icmp/endpoint.go b/pkg/tcpip/transport/icmp/endpoint.go index 8f2e3aa20..00840cfcf 100644 --- a/pkg/tcpip/transport/icmp/endpoint.go +++ b/pkg/tcpip/transport/icmp/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/icmp/endpoint_state.go b/pkg/tcpip/transport/icmp/endpoint_state.go index 8a7909246..332b3cd33 100644 --- a/pkg/tcpip/transport/icmp/endpoint_state.go +++ b/pkg/tcpip/transport/icmp/endpoint_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/icmp/protocol.go b/pkg/tcpip/transport/icmp/protocol.go index 09ee2f892..954fde9d8 100644 --- a/pkg/tcpip/transport/icmp/protocol.go +++ b/pkg/tcpip/transport/icmp/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/raw/raw.go b/pkg/tcpip/transport/raw/raw.go index f0f60ce91..7004c7ff4 100644 --- a/pkg/tcpip/transport/raw/raw.go +++ b/pkg/tcpip/transport/raw/raw.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/tcpip/transport/raw/state.go b/pkg/tcpip/transport/raw/state.go index e3891a8b8..e8907ebb1 100644 --- a/pkg/tcpip/transport/raw/state.go +++ b/pkg/tcpip/transport/raw/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/accept.go b/pkg/tcpip/transport/tcp/accept.go index a3894ed8f..e506d7133 100644 --- a/pkg/tcpip/transport/tcp/accept.go +++ b/pkg/tcpip/transport/tcp/accept.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/connect.go b/pkg/tcpip/transport/tcp/connect.go index 6c4a4d95e..eaa67aeb7 100644 --- a/pkg/tcpip/transport/tcp/connect.go +++ b/pkg/tcpip/transport/tcp/connect.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/cubic.go b/pkg/tcpip/transport/tcp/cubic.go index 003525d86..e618cd2b9 100644 --- a/pkg/tcpip/transport/tcp/cubic.go +++ b/pkg/tcpip/transport/tcp/cubic.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/dual_stack_test.go b/pkg/tcpip/transport/tcp/dual_stack_test.go index 2886cc707..43bcfa070 100644 --- a/pkg/tcpip/transport/tcp/dual_stack_test.go +++ b/pkg/tcpip/transport/tcp/dual_stack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/endpoint.go b/pkg/tcpip/transport/tcp/endpoint.go index 09eff5be1..982f491cc 100644 --- a/pkg/tcpip/transport/tcp/endpoint.go +++ b/pkg/tcpip/transport/tcp/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/endpoint_state.go b/pkg/tcpip/transport/tcp/endpoint_state.go index 7f9dabb4d..27b0be046 100644 --- a/pkg/tcpip/transport/tcp/endpoint_state.go +++ b/pkg/tcpip/transport/tcp/endpoint_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/forwarder.go b/pkg/tcpip/transport/tcp/forwarder.go index 6a7efaf1d..e088e24cb 100644 --- a/pkg/tcpip/transport/tcp/forwarder.go +++ b/pkg/tcpip/transport/tcp/forwarder.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/protocol.go b/pkg/tcpip/transport/tcp/protocol.go index b5fb160bc..b86473891 100644 --- a/pkg/tcpip/transport/tcp/protocol.go +++ b/pkg/tcpip/transport/tcp/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/rcv.go b/pkg/tcpip/transport/tcp/rcv.go index fa6bdddba..b08a0e356 100644 --- a/pkg/tcpip/transport/tcp/rcv.go +++ b/pkg/tcpip/transport/tcp/rcv.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/reno.go b/pkg/tcpip/transport/tcp/reno.go index e4f8b7d5a..f83ebc717 100644 --- a/pkg/tcpip/transport/tcp/reno.go +++ b/pkg/tcpip/transport/tcp/reno.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/sack.go b/pkg/tcpip/transport/tcp/sack.go index 24e48fe7b..6a013d99b 100644 --- a/pkg/tcpip/transport/tcp/sack.go +++ b/pkg/tcpip/transport/tcp/sack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/sack_scoreboard.go b/pkg/tcpip/transport/tcp/sack_scoreboard.go index 21878ad82..99560d5b4 100644 --- a/pkg/tcpip/transport/tcp/sack_scoreboard.go +++ b/pkg/tcpip/transport/tcp/sack_scoreboard.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/sack_scoreboard_test.go b/pkg/tcpip/transport/tcp/sack_scoreboard_test.go index 3cf2ff451..8f6890cdf 100644 --- a/pkg/tcpip/transport/tcp/sack_scoreboard_test.go +++ b/pkg/tcpip/transport/tcp/sack_scoreboard_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/segment.go b/pkg/tcpip/transport/tcp/segment.go index c603fe713..187effb6b 100644 --- a/pkg/tcpip/transport/tcp/segment.go +++ b/pkg/tcpip/transport/tcp/segment.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/segment_heap.go b/pkg/tcpip/transport/tcp/segment_heap.go index 98422fadf..9fd061d7d 100644 --- a/pkg/tcpip/transport/tcp/segment_heap.go +++ b/pkg/tcpip/transport/tcp/segment_heap.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/segment_queue.go b/pkg/tcpip/transport/tcp/segment_queue.go index 0c637d7ad..3b020e580 100644 --- a/pkg/tcpip/transport/tcp/segment_queue.go +++ b/pkg/tcpip/transport/tcp/segment_queue.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/segment_state.go b/pkg/tcpip/transport/tcp/segment_state.go index 68b049f06..dd7e14aa6 100644 --- a/pkg/tcpip/transport/tcp/segment_state.go +++ b/pkg/tcpip/transport/tcp/segment_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go index 6317748cf..50743670e 100644 --- a/pkg/tcpip/transport/tcp/snd.go +++ b/pkg/tcpip/transport/tcp/snd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/snd_state.go b/pkg/tcpip/transport/tcp/snd_state.go index 86bbd643f..12eff8afc 100644 --- a/pkg/tcpip/transport/tcp/snd_state.go +++ b/pkg/tcpip/transport/tcp/snd_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/tcp_sack_test.go b/pkg/tcpip/transport/tcp/tcp_sack_test.go index 06b0702c5..dbfbd5c4f 100644 --- a/pkg/tcpip/transport/tcp/tcp_sack_test.go +++ b/pkg/tcpip/transport/tcp/tcp_sack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/tcp_test.go b/pkg/tcpip/transport/tcp/tcp_test.go index c5732ad1c..a8b290dae 100644 --- a/pkg/tcpip/transport/tcp/tcp_test.go +++ b/pkg/tcpip/transport/tcp/tcp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/tcp_timestamp_test.go b/pkg/tcpip/transport/tcp/tcp_timestamp_test.go index 87c640967..039bbcfba 100644 --- a/pkg/tcpip/transport/tcp/tcp_timestamp_test.go +++ b/pkg/tcpip/transport/tcp/tcp_timestamp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/testing/context/context.go b/pkg/tcpip/transport/tcp/testing/context/context.go index 6e2fed880..fa721a7f8 100644 --- a/pkg/tcpip/transport/tcp/testing/context/context.go +++ b/pkg/tcpip/transport/tcp/testing/context/context.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcp/timer.go b/pkg/tcpip/transport/tcp/timer.go index 38240d2d5..fc1c7cbd2 100644 --- a/pkg/tcpip/transport/tcp/timer.go +++ b/pkg/tcpip/transport/tcp/timer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go index b94568fb1..f1dcd36d5 100644 --- a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go +++ b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go index aaeae9b18..435e136de 100644 --- a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go +++ b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/udp/endpoint.go b/pkg/tcpip/transport/udp/endpoint.go index 1f9251de3..db65a4e88 100644 --- a/pkg/tcpip/transport/udp/endpoint.go +++ b/pkg/tcpip/transport/udp/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/udp/endpoint_state.go b/pkg/tcpip/transport/udp/endpoint_state.go index b2daaf751..163dcbc13 100644 --- a/pkg/tcpip/transport/udp/endpoint_state.go +++ b/pkg/tcpip/transport/udp/endpoint_state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/udp/forwarder.go b/pkg/tcpip/transport/udp/forwarder.go index d80c47e34..25bdd2929 100644 --- a/pkg/tcpip/transport/udp/forwarder.go +++ b/pkg/tcpip/transport/udp/forwarder.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/pkg/tcpip/transport/udp/protocol.go b/pkg/tcpip/transport/udp/protocol.go index 616a9f388..8b47cce17 100644 --- a/pkg/tcpip/transport/udp/protocol.go +++ b/pkg/tcpip/transport/udp/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tcpip/transport/udp/udp_test.go b/pkg/tcpip/transport/udp/udp_test.go index 2f4e94c58..86a8fa19b 100644 --- a/pkg/tcpip/transport/udp/udp_test.go +++ b/pkg/tcpip/transport/udp/udp_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tmutex/tmutex.go b/pkg/tmutex/tmutex.go index df61d89f5..c4685020d 100644 --- a/pkg/tmutex/tmutex.go +++ b/pkg/tmutex/tmutex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/tmutex/tmutex_test.go b/pkg/tmutex/tmutex_test.go index a4537cb3b..ce34c7962 100644 --- a/pkg/tmutex/tmutex_test.go +++ b/pkg/tmutex/tmutex_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/unet/unet.go b/pkg/unet/unet.go index 114fb8c5b..2aa1af4ff 100644 --- a/pkg/unet/unet.go +++ b/pkg/unet/unet.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/unet/unet_test.go b/pkg/unet/unet_test.go index db5485539..763b23c7c 100644 --- a/pkg/unet/unet_test.go +++ b/pkg/unet/unet_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/unet/unet_unsafe.go b/pkg/unet/unet_unsafe.go index 1d6ec286c..fa0916439 100644 --- a/pkg/unet/unet_unsafe.go +++ b/pkg/unet/unet_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/urpc/urpc.go b/pkg/urpc/urpc.go index 719f0e92f..0f155ec74 100644 --- a/pkg/urpc/urpc.go +++ b/pkg/urpc/urpc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/urpc/urpc_test.go b/pkg/urpc/urpc_test.go index f1b9a85ca..5bf2c5ed2 100644 --- a/pkg/urpc/urpc_test.go +++ b/pkg/urpc/urpc_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/waiter/waiter.go b/pkg/waiter/waiter.go index a6c9dff3c..8a65ed164 100644 --- a/pkg/waiter/waiter.go +++ b/pkg/waiter/waiter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/pkg/waiter/waiter_test.go b/pkg/waiter/waiter_test.go index 60853f9c1..c1b94a4f3 100644 --- a/pkg/waiter/waiter_test.go +++ b/pkg/waiter/waiter_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/compat.go b/runsc/boot/compat.go index b3499bcde..c1b33c551 100644 --- a/runsc/boot/compat.go +++ b/runsc/boot/compat.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/compat_amd64.go b/runsc/boot/compat_amd64.go index 0c9472f18..99df5e614 100644 --- a/runsc/boot/compat_amd64.go +++ b/runsc/boot/compat_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/compat_test.go b/runsc/boot/compat_test.go index f1940dd72..ccec3d20c 100644 --- a/runsc/boot/compat_test.go +++ b/runsc/boot/compat_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/config.go b/runsc/boot/config.go index ba47effc1..b6771de30 100644 --- a/runsc/boot/config.go +++ b/runsc/boot/config.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 712c50ee9..ab7c58838 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/debug.go b/runsc/boot/debug.go index d224d08b7..79f7387ac 100644 --- a/runsc/boot/debug.go +++ b/runsc/boot/debug.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/events.go b/runsc/boot/events.go index 717adfedd..ffd99f5e9 100644 --- a/runsc/boot/events.go +++ b/runsc/boot/events.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/fds.go b/runsc/boot/fds.go index a3d21d963..4e428b49c 100644 --- a/runsc/boot/fds.go +++ b/runsc/boot/fds.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go index 9c72e3b1a..652da1cef 100644 --- a/runsc/boot/filter/config.go +++ b/runsc/boot/filter/config.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/filter/extra_filters.go b/runsc/boot/filter/extra_filters.go index 67f3101fe..5c5ec4e06 100644 --- a/runsc/boot/filter/extra_filters.go +++ b/runsc/boot/filter/extra_filters.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/filter/extra_filters_msan.go b/runsc/boot/filter/extra_filters_msan.go index fb95283ab..ac5a0f1aa 100644 --- a/runsc/boot/filter/extra_filters_msan.go +++ b/runsc/boot/filter/extra_filters_msan.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/filter/extra_filters_race.go b/runsc/boot/filter/extra_filters_race.go index 02a122c95..ba3c1ce87 100644 --- a/runsc/boot/filter/extra_filters_race.go +++ b/runsc/boot/filter/extra_filters_race.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/filter/filter.go b/runsc/boot/filter/filter.go index fb197f9b1..17479e0dd 100644 --- a/runsc/boot/filter/filter.go +++ b/runsc/boot/filter/filter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 07061b9b3..aeb1c52cc 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/limits.go b/runsc/boot/limits.go index 32e62cdf7..3364aa5e6 100644 --- a/runsc/boot/limits.go +++ b/runsc/boot/limits.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 75ec19c32..0b5be0a42 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index 01578cfc5..9a864ad3f 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/network.go b/runsc/boot/network.go index 35baa36ad..598ec969e 100644 --- a/runsc/boot/network.go +++ b/runsc/boot/network.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/boot/strace.go b/runsc/boot/strace.go index 028bcc1f4..19c7f8fbd 100644 --- a/runsc/boot/strace.go +++ b/runsc/boot/strace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go index 2b338b6c6..7431b17d6 100644 --- a/runsc/cgroup/cgroup.go +++ b/runsc/cgroup/cgroup.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cgroup/cgroup_test.go b/runsc/cgroup/cgroup_test.go index ecc184f74..548c80e9a 100644 --- a/runsc/cgroup/cgroup_test.go +++ b/runsc/cgroup/cgroup_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index ff2fa2fb9..ac937f7bc 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/capability.go b/runsc/cmd/capability.go index e5da021e5..312e5b471 100644 --- a/runsc/cmd/capability.go +++ b/runsc/cmd/capability.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/capability_test.go b/runsc/cmd/capability_test.go index dd278b32d..ee74d33d8 100644 --- a/runsc/cmd/capability_test.go +++ b/runsc/cmd/capability_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/checkpoint.go b/runsc/cmd/checkpoint.go index f722df055..96d3c3378 100644 --- a/runsc/cmd/checkpoint.go +++ b/runsc/cmd/checkpoint.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/chroot.go b/runsc/cmd/chroot.go index ed1dafef1..1a774db04 100644 --- a/runsc/cmd/chroot.go +++ b/runsc/cmd/chroot.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/runsc/cmd/cmd.go b/runsc/cmd/cmd.go index 208cf5304..aa7b1a636 100644 --- a/runsc/cmd/cmd.go +++ b/runsc/cmd/cmd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 30c8fa283..629c198fd 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/debug.go b/runsc/cmd/debug.go index 3ee9a9b49..000f694c7 100644 --- a/runsc/cmd/debug.go +++ b/runsc/cmd/debug.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/delete.go b/runsc/cmd/delete.go index 3206b267a..9039723e9 100644 --- a/runsc/cmd/delete.go +++ b/runsc/cmd/delete.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/delete_test.go b/runsc/cmd/delete_test.go index 4a5b4774a..45fc91016 100644 --- a/runsc/cmd/delete_test.go +++ b/runsc/cmd/delete_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/do.go b/runsc/cmd/do.go index 343461130..67d415733 100644 --- a/runsc/cmd/do.go +++ b/runsc/cmd/do.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/events.go b/runsc/cmd/events.go index 208d2f74b..c6bc8fc3a 100644 --- a/runsc/cmd/events.go +++ b/runsc/cmd/events.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index 718d01067..ad2508405 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/exec_test.go b/runsc/cmd/exec_test.go index 686c5e150..6f0f258c0 100644 --- a/runsc/cmd/exec_test.go +++ b/runsc/cmd/exec_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index 82487887c..bccb29397 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/gofer_test.go b/runsc/cmd/gofer_test.go index 8e692feb9..cbea7f127 100644 --- a/runsc/cmd/gofer_test.go +++ b/runsc/cmd/gofer_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/kill.go b/runsc/cmd/kill.go index e67f82473..aed5f3291 100644 --- a/runsc/cmd/kill.go +++ b/runsc/cmd/kill.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/list.go b/runsc/cmd/list.go index 1dcea2af0..1f5ca2473 100644 --- a/runsc/cmd/list.go +++ b/runsc/cmd/list.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/path.go b/runsc/cmd/path.go index 1276f0dbd..0e9ef7fa5 100644 --- a/runsc/cmd/path.go +++ b/runsc/cmd/path.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/pause.go b/runsc/cmd/pause.go index 2c93e5f3e..11b36aa10 100644 --- a/runsc/cmd/pause.go +++ b/runsc/cmd/pause.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/ps.go b/runsc/cmd/ps.go index 060d796f2..3a3e6f17a 100644 --- a/runsc/cmd/ps.go +++ b/runsc/cmd/ps.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/restore.go b/runsc/cmd/restore.go index 66b23c38e..27b06713a 100644 --- a/runsc/cmd/restore.go +++ b/runsc/cmd/restore.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/resume.go b/runsc/cmd/resume.go index 5551d1450..9a2ade41e 100644 --- a/runsc/cmd/resume.go +++ b/runsc/cmd/resume.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index be1c1b678..4d5f5c139 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/spec.go b/runsc/cmd/spec.go index 063bd39c5..344da13ba 100644 --- a/runsc/cmd/spec.go +++ b/runsc/cmd/spec.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/start.go b/runsc/cmd/start.go index 9e2e0c11d..657726251 100644 --- a/runsc/cmd/start.go +++ b/runsc/cmd/start.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/state.go b/runsc/cmd/state.go index c3ef65ab5..f0d449b19 100644 --- a/runsc/cmd/state.go +++ b/runsc/cmd/state.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/cmd/wait.go b/runsc/cmd/wait.go index 6498dd15c..a55a682f3 100644 --- a/runsc/cmd/wait.go +++ b/runsc/cmd/wait.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/console/console.go b/runsc/console/console.go index 2eb9a8807..64b23639a 100644 --- a/runsc/console/console.go +++ b/runsc/console/console.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/container/console_test.go b/runsc/container/console_test.go index 0b0dfb4cb..b8af27c15 100644 --- a/runsc/container/console_test.go +++ b/runsc/container/console_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/container/container.go b/runsc/container/container.go index a30c217f7..884bbc0fb 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 603c4d929..9458dbb90 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/container/hook.go b/runsc/container/hook.go index 6b9e5550a..acae6781e 100644 --- a/runsc/container/hook.go +++ b/runsc/container/hook.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index 8922e6dbe..e554237cf 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/container/shared_volume_test.go b/runsc/container/shared_volume_test.go index 8f81ed630..9d5a592a5 100644 --- a/runsc/container/shared_volume_test.go +++ b/runsc/container/shared_volume_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/runsc/container/status.go b/runsc/container/status.go index 234ffb0dd..91d9112f1 100644 --- a/runsc/container/status.go +++ b/runsc/container/status.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/container/test_app.go b/runsc/container/test_app.go index b5071ada6..62923f1ef 100644 --- a/runsc/container/test_app.go +++ b/runsc/container/test_app.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/filter/config.go b/runsc/fsgofer/filter/config.go index 75a087848..a1ad49fb2 100644 --- a/runsc/fsgofer/filter/config.go +++ b/runsc/fsgofer/filter/config.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/filter/extra_filters.go b/runsc/fsgofer/filter/extra_filters.go index 67f3101fe..5c5ec4e06 100644 --- a/runsc/fsgofer/filter/extra_filters.go +++ b/runsc/fsgofer/filter/extra_filters.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/filter/extra_filters_msan.go b/runsc/fsgofer/filter/extra_filters_msan.go index 7e142b790..553060bc3 100644 --- a/runsc/fsgofer/filter/extra_filters_msan.go +++ b/runsc/fsgofer/filter/extra_filters_msan.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/filter/extra_filters_race.go b/runsc/fsgofer/filter/extra_filters_race.go index 3cd29472a..28555f898 100644 --- a/runsc/fsgofer/filter/extra_filters_race.go +++ b/runsc/fsgofer/filter/extra_filters_race.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/filter/filter.go b/runsc/fsgofer/filter/filter.go index c120d57a6..ff8154369 100644 --- a/runsc/fsgofer/filter/filter.go +++ b/runsc/fsgofer/filter/filter.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go index c964a2a3b..158f22ddc 100644 --- a/runsc/fsgofer/fsgofer.go +++ b/runsc/fsgofer/fsgofer.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/fsgofer_test.go b/runsc/fsgofer/fsgofer_test.go index e74df7ede..695836927 100644 --- a/runsc/fsgofer/fsgofer_test.go +++ b/runsc/fsgofer/fsgofer_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/fsgofer/fsgofer_unsafe.go b/runsc/fsgofer/fsgofer_unsafe.go index 94413db86..58af5e44d 100644 --- a/runsc/fsgofer/fsgofer_unsafe.go +++ b/runsc/fsgofer/fsgofer_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/main.go b/runsc/main.go index b35726a74..11bc73f75 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index 6c6b665a0..2a68d7043 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/sandbox/network_unsafe.go b/runsc/sandbox/network_unsafe.go index f7447f002..2a2a0fb7e 100644 --- a/runsc/sandbox/network_unsafe.go +++ b/runsc/sandbox/network_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 48a0dafe2..dac35ca0b 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/specutils/fs.go b/runsc/specutils/fs.go index 98c3b19c0..1f3afb4e4 100644 --- a/runsc/specutils/fs.go +++ b/runsc/specutils/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go index 35da789f4..7d194335c 100644 --- a/runsc/specutils/namespace.go +++ b/runsc/specutils/namespace.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index ac85bec71..c72207fb4 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go index 02af6e6ad..2c86fffe8 100644 --- a/runsc/specutils/specutils_test.go +++ b/runsc/specutils/specutils_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/image/image.go b/runsc/test/image/image.go index bcb6f876f..297f1ab92 100644 --- a/runsc/test/image/image.go +++ b/runsc/test/image/image.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/image/image_test.go b/runsc/test/image/image_test.go index f7e750d71..0c45602f9 100644 --- a/runsc/test/image/image_test.go +++ b/runsc/test/image/image_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/image/mysql.sql b/runsc/test/image/mysql.sql index c1271e719..51554b98d 100644 --- a/runsc/test/image/mysql.sql +++ b/runsc/test/image/mysql.sql @@ -1,4 +1,4 @@ -# Copyright 2018 Google LLC +# 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. diff --git a/runsc/test/image/ruby.rb b/runsc/test/image/ruby.rb index 25d1ac129..aced49c6d 100644 --- a/runsc/test/image/ruby.rb +++ b/runsc/test/image/ruby.rb @@ -1,4 +1,4 @@ -# Copyright 2018 Google LLC +# 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. diff --git a/runsc/test/image/ruby.sh b/runsc/test/image/ruby.sh index d3a9b5656..ebe8d5b0e 100644 --- a/runsc/test/image/ruby.sh +++ b/runsc/test/image/ruby.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/runsc/test/install.sh b/runsc/test/install.sh index 32e1e884e..457df2d26 100755 --- a/runsc/test/install.sh +++ b/runsc/test/install.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/runsc/test/integration/exec_test.go b/runsc/test/integration/exec_test.go index d87957e2d..7af064d79 100644 --- a/runsc/test/integration/exec_test.go +++ b/runsc/test/integration/exec_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/integration/integration.go b/runsc/test/integration/integration.go index e15321c87..4cd5f6c24 100644 --- a/runsc/test/integration/integration.go +++ b/runsc/test/integration/integration.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/integration/integration_test.go b/runsc/test/integration/integration_test.go index 4a2770d48..b2e86aacc 100644 --- a/runsc/test/integration/integration_test.go +++ b/runsc/test/integration/integration_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/cgroup_test.go b/runsc/test/root/cgroup_test.go index 91839048c..edb6dee1d 100644 --- a/runsc/test/root/cgroup_test.go +++ b/runsc/test/root/cgroup_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/chroot_test.go b/runsc/test/root/chroot_test.go index 0deca0532..da2f473b9 100644 --- a/runsc/test/root/chroot_test.go +++ b/runsc/test/root/chroot_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/crictl_test.go b/runsc/test/root/crictl_test.go index 37fe53ba3..3cc176104 100644 --- a/runsc/test/root/crictl_test.go +++ b/runsc/test/root/crictl_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/root.go b/runsc/test/root/root.go index 586ea0fe3..349c752cc 100644 --- a/runsc/test/root/root.go +++ b/runsc/test/root/root.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/testdata/busybox.go b/runsc/test/root/testdata/busybox.go index 544571c63..e4dbd2843 100644 --- a/runsc/test/root/testdata/busybox.go +++ b/runsc/test/root/testdata/busybox.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/testdata/containerd_config.go b/runsc/test/root/testdata/containerd_config.go index 949354987..e12f1ec88 100644 --- a/runsc/test/root/testdata/containerd_config.go +++ b/runsc/test/root/testdata/containerd_config.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/testdata/httpd.go b/runsc/test/root/testdata/httpd.go index f65b1da5d..45d5e33d4 100644 --- a/runsc/test/root/testdata/httpd.go +++ b/runsc/test/root/testdata/httpd.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/testdata/httpd_mount_paths.go b/runsc/test/root/testdata/httpd_mount_paths.go index 5ca14340e..ac3f4446a 100644 --- a/runsc/test/root/testdata/httpd_mount_paths.go +++ b/runsc/test/root/testdata/httpd_mount_paths.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/root/testdata/sandbox.go b/runsc/test/root/testdata/sandbox.go index 194242a27..0db210370 100644 --- a/runsc/test/root/testdata/sandbox.go +++ b/runsc/test/root/testdata/sandbox.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/testutil/crictl.go b/runsc/test/testutil/crictl.go index 84bb4475a..4f9ee0c05 100644 --- a/runsc/test/testutil/crictl.go +++ b/runsc/test/testutil/crictl.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/testutil/docker.go b/runsc/test/testutil/docker.go index b651319ed..29ef505b4 100644 --- a/runsc/test/testutil/docker.go +++ b/runsc/test/testutil/docker.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 79f0a8b6b..6a4c045a8 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/test/testutil/testutil_race.go b/runsc/test/testutil/testutil_race.go index 9267af150..86db6ffa1 100644 --- a/runsc/test/testutil/testutil_race.go +++ b/runsc/test/testutil/testutil_race.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/tools/dockercfg/dockercfg.go b/runsc/tools/dockercfg/dockercfg.go index cc7a67816..6fb134558 100644 --- a/runsc/tools/dockercfg/dockercfg.go +++ b/runsc/tools/dockercfg/dockercfg.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/runsc/version.go b/runsc/version.go index 4894f2de6..ce0573a9b 100644 --- a/runsc/version.go +++ b/runsc/version.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/gtest/gtest.go b/test/syscalls/gtest/gtest.go index dfe5037cd..bdec8eb07 100644 --- a/test/syscalls/gtest/gtest.go +++ b/test/syscalls/gtest/gtest.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc index 78baf548e..a7cbee06b 100644 --- a/test/syscalls/linux/32bit.cc +++ b/test/syscalls/linux/32bit.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/accept_bind.cc b/test/syscalls/linux/accept_bind.cc index c2bb4a7ce..56377feab 100644 --- a/test/syscalls/linux/accept_bind.cc +++ b/test/syscalls/linux/accept_bind.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/accept_bind_stream.cc b/test/syscalls/linux/accept_bind_stream.cc index 1501e526e..b6cdb3f4f 100644 --- a/test/syscalls/linux/accept_bind_stream.cc +++ b/test/syscalls/linux/accept_bind_stream.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/access.cc b/test/syscalls/linux/access.cc index 6ea070a5d..bcc25cef4 100644 --- a/test/syscalls/linux/access.cc +++ b/test/syscalls/linux/access.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/affinity.cc b/test/syscalls/linux/affinity.cc index 81bd9bcb5..f2d8375b6 100644 --- a/test/syscalls/linux/affinity.cc +++ b/test/syscalls/linux/affinity.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/aio.cc b/test/syscalls/linux/aio.cc index b96aab9b9..68dc05417 100644 --- a/test/syscalls/linux/aio.cc +++ b/test/syscalls/linux/aio.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/alarm.cc b/test/syscalls/linux/alarm.cc index e0ddbb415..d89269985 100644 --- a/test/syscalls/linux/alarm.cc +++ b/test/syscalls/linux/alarm.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/arch_prctl.cc b/test/syscalls/linux/arch_prctl.cc index 5687ceb86..81bf5a775 100644 --- a/test/syscalls/linux/arch_prctl.cc +++ b/test/syscalls/linux/arch_prctl.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/bad.cc b/test/syscalls/linux/bad.cc index a2634a8bf..f246a799e 100644 --- a/test/syscalls/linux/bad.cc +++ b/test/syscalls/linux/bad.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/base_poll_test.cc b/test/syscalls/linux/base_poll_test.cc index bba0108ea..ab7a19dd0 100644 --- a/test/syscalls/linux/base_poll_test.cc +++ b/test/syscalls/linux/base_poll_test.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/base_poll_test.h b/test/syscalls/linux/base_poll_test.h index 9b9b81933..088831f9f 100644 --- a/test/syscalls/linux/base_poll_test.h +++ b/test/syscalls/linux/base_poll_test.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/bind.cc b/test/syscalls/linux/bind.cc index f5aa9c500..de8cca53b 100644 --- a/test/syscalls/linux/bind.cc +++ b/test/syscalls/linux/bind.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/brk.cc b/test/syscalls/linux/brk.cc index 33d353959..a03a44465 100644 --- a/test/syscalls/linux/brk.cc +++ b/test/syscalls/linux/brk.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/chdir.cc b/test/syscalls/linux/chdir.cc index a4b54f0ee..3182c228b 100644 --- a/test/syscalls/linux/chdir.cc +++ b/test/syscalls/linux/chdir.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/chmod.cc b/test/syscalls/linux/chmod.cc index 2f42fe326..79e98597f 100644 --- a/test/syscalls/linux/chmod.cc +++ b/test/syscalls/linux/chmod.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/chown.cc b/test/syscalls/linux/chown.cc index ad892cf6a..eb1762ddf 100644 --- a/test/syscalls/linux/chown.cc +++ b/test/syscalls/linux/chown.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/chroot.cc b/test/syscalls/linux/chroot.cc index 6c200f63e..a4354ff62 100644 --- a/test/syscalls/linux/chroot.cc +++ b/test/syscalls/linux/chroot.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/clock_getres.cc b/test/syscalls/linux/clock_getres.cc index 8f8842299..c408b936c 100644 --- a/test/syscalls/linux/clock_getres.cc +++ b/test/syscalls/linux/clock_getres.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/clock_gettime.cc b/test/syscalls/linux/clock_gettime.cc index 4ecb5f5b1..082ae1c39 100644 --- a/test/syscalls/linux/clock_gettime.cc +++ b/test/syscalls/linux/clock_gettime.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/clock_nanosleep.cc b/test/syscalls/linux/clock_nanosleep.cc index 61c67a5ff..52a69d230 100644 --- a/test/syscalls/linux/clock_nanosleep.cc +++ b/test/syscalls/linux/clock_nanosleep.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/concurrency.cc b/test/syscalls/linux/concurrency.cc index 7978845c1..4e0a13f8b 100644 --- a/test/syscalls/linux/concurrency.cc +++ b/test/syscalls/linux/concurrency.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/creat.cc b/test/syscalls/linux/creat.cc index df2cc0d5c..3c270d6da 100644 --- a/test/syscalls/linux/creat.cc +++ b/test/syscalls/linux/creat.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/dev.cc b/test/syscalls/linux/dev.cc index a140d3b30..b86ebe233 100644 --- a/test/syscalls/linux/dev.cc +++ b/test/syscalls/linux/dev.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/dup.cc b/test/syscalls/linux/dup.cc index e8de2f4c4..4f773bc75 100644 --- a/test/syscalls/linux/dup.cc +++ b/test/syscalls/linux/dup.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc index b4a3bfcba..a4f8f3cec 100644 --- a/test/syscalls/linux/epoll.cc +++ b/test/syscalls/linux/epoll.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc index 8111da30e..5e5c39d44 100644 --- a/test/syscalls/linux/eventfd.cc +++ b/test/syscalls/linux/eventfd.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exceptions.cc b/test/syscalls/linux/exceptions.cc index 3f0aa8bf1..0da4c817d 100644 --- a/test/syscalls/linux/exceptions.cc +++ b/test/syscalls/linux/exceptions.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exec.cc b/test/syscalls/linux/exec.cc index 30bc4b608..06c322a99 100644 --- a/test/syscalls/linux/exec.cc +++ b/test/syscalls/linux/exec.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exec.h b/test/syscalls/linux/exec.h index b82bfffd1..5c0f7e654 100644 --- a/test/syscalls/linux/exec.h +++ b/test/syscalls/linux/exec.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exec_assert_closed_workload.cc b/test/syscalls/linux/exec_assert_closed_workload.cc index 4448431e1..95643618d 100644 --- a/test/syscalls/linux/exec_assert_closed_workload.cc +++ b/test/syscalls/linux/exec_assert_closed_workload.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exec_basic_workload.cc b/test/syscalls/linux/exec_basic_workload.cc index d4bdf511f..1bbd6437e 100644 --- a/test/syscalls/linux/exec_basic_workload.cc +++ b/test/syscalls/linux/exec_basic_workload.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exec_binary.cc b/test/syscalls/linux/exec_binary.cc index c10d85398..bdd6eb10b 100644 --- a/test/syscalls/linux/exec_binary.cc +++ b/test/syscalls/linux/exec_binary.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exec_proc_exe_workload.cc b/test/syscalls/linux/exec_proc_exe_workload.cc index b9a4ac749..b3fbd5042 100644 --- a/test/syscalls/linux/exec_proc_exe_workload.cc +++ b/test/syscalls/linux/exec_proc_exe_workload.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exec_state_workload.cc b/test/syscalls/linux/exec_state_workload.cc index b66e22565..725c2977f 100644 --- a/test/syscalls/linux/exec_state_workload.cc +++ b/test/syscalls/linux/exec_state_workload.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exit.cc b/test/syscalls/linux/exit.cc index 7246a7b3b..99de2b376 100644 --- a/test/syscalls/linux/exit.cc +++ b/test/syscalls/linux/exit.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/exit_script.sh b/test/syscalls/linux/exit_script.sh index f014fcf99..527518e06 100755 --- a/test/syscalls/linux/exit_script.sh +++ b/test/syscalls/linux/exit_script.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/test/syscalls/linux/fadvise64.cc b/test/syscalls/linux/fadvise64.cc index 041e8b7b6..2af7aa6d9 100644 --- a/test/syscalls/linux/fadvise64.cc +++ b/test/syscalls/linux/fadvise64.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fallocate.cc b/test/syscalls/linux/fallocate.cc index e51538734..61b8acc7a 100644 --- a/test/syscalls/linux/fallocate.cc +++ b/test/syscalls/linux/fallocate.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fault.cc b/test/syscalls/linux/fault.cc index cfa7d0d1f..f6e19026f 100644 --- a/test/syscalls/linux/fault.cc +++ b/test/syscalls/linux/fault.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fchdir.cc b/test/syscalls/linux/fchdir.cc index 2b13e36c3..08bcae1e8 100644 --- a/test/syscalls/linux/fchdir.cc +++ b/test/syscalls/linux/fchdir.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc index 32a90a163..2f8e7c9dd 100644 --- a/test/syscalls/linux/fcntl.cc +++ b/test/syscalls/linux/fcntl.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/file_base.h b/test/syscalls/linux/file_base.h index 43f568111..b5b972c07 100644 --- a/test/syscalls/linux/file_base.h +++ b/test/syscalls/linux/file_base.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/flock.cc b/test/syscalls/linux/flock.cc index 1388d3839..d89cfcbd7 100644 --- a/test/syscalls/linux/flock.cc +++ b/test/syscalls/linux/flock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fork.cc b/test/syscalls/linux/fork.cc index 73ac885b5..dd6e1a422 100644 --- a/test/syscalls/linux/fork.cc +++ b/test/syscalls/linux/fork.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fpsig_fork.cc b/test/syscalls/linux/fpsig_fork.cc index e8f1dfa8a..e7e9f06a1 100644 --- a/test/syscalls/linux/fpsig_fork.cc +++ b/test/syscalls/linux/fpsig_fork.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fpsig_nested.cc b/test/syscalls/linux/fpsig_nested.cc index 2fa40b42d..395463aed 100644 --- a/test/syscalls/linux/fpsig_nested.cc +++ b/test/syscalls/linux/fpsig_nested.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/fsync.cc b/test/syscalls/linux/fsync.cc index b34229248..e7e057f06 100644 --- a/test/syscalls/linux/fsync.cc +++ b/test/syscalls/linux/fsync.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/futex.cc b/test/syscalls/linux/futex.cc index c7a709a0a..bfec95466 100644 --- a/test/syscalls/linux/futex.cc +++ b/test/syscalls/linux/futex.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/getcpu.cc b/test/syscalls/linux/getcpu.cc index 3a52b25fa..f4d94bd6a 100644 --- a/test/syscalls/linux/getcpu.cc +++ b/test/syscalls/linux/getcpu.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/getdents.cc b/test/syscalls/linux/getdents.cc index e8a7bcd43..d146c8db7 100644 --- a/test/syscalls/linux/getdents.cc +++ b/test/syscalls/linux/getdents.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/getrandom.cc b/test/syscalls/linux/getrandom.cc index be5325497..f97f60029 100644 --- a/test/syscalls/linux/getrandom.cc +++ b/test/syscalls/linux/getrandom.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/getrusage.cc b/test/syscalls/linux/getrusage.cc index 1ae603858..9bdb1e4cd 100644 --- a/test/syscalls/linux/getrusage.cc +++ b/test/syscalls/linux/getrusage.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc index b99d339e5..6a3539e22 100644 --- a/test/syscalls/linux/inotify.cc +++ b/test/syscalls/linux/inotify.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/ioctl.cc b/test/syscalls/linux/ioctl.cc index c7741a177..c525d41d2 100644 --- a/test/syscalls/linux/ioctl.cc +++ b/test/syscalls/linux/ioctl.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc index 0a149c2e5..7612919d4 100644 --- a/test/syscalls/linux/ip_socket_test_util.cc +++ b/test/syscalls/linux/ip_socket_test_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h index cac790e64..6898effb8 100644 --- a/test/syscalls/linux/ip_socket_test_util.h +++ b/test/syscalls/linux/ip_socket_test_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/itimer.cc b/test/syscalls/linux/itimer.cc index ddfbc28fc..57ffd1595 100644 --- a/test/syscalls/linux/itimer.cc +++ b/test/syscalls/linux/itimer.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/kill.cc b/test/syscalls/linux/kill.cc index cd98de41f..18ad923b8 100644 --- a/test/syscalls/linux/kill.cc +++ b/test/syscalls/linux/kill.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc index ed74437bc..a91703070 100644 --- a/test/syscalls/linux/link.cc +++ b/test/syscalls/linux/link.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/lseek.cc b/test/syscalls/linux/lseek.cc index 6a4f1423c..a8af8e545 100644 --- a/test/syscalls/linux/lseek.cc +++ b/test/syscalls/linux/lseek.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/madvise.cc b/test/syscalls/linux/madvise.cc index a79c8c75d..f6ad4d18b 100644 --- a/test/syscalls/linux/madvise.cc +++ b/test/syscalls/linux/madvise.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/memfd.cc b/test/syscalls/linux/memfd.cc index c2513682d..7e103124b 100644 --- a/test/syscalls/linux/memfd.cc +++ b/test/syscalls/linux/memfd.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/memory_accounting.cc b/test/syscalls/linux/memory_accounting.cc index b4b680c34..a6e20f9c3 100644 --- a/test/syscalls/linux/memory_accounting.cc +++ b/test/syscalls/linux/memory_accounting.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc index 9f8033bdf..4ac4cb88f 100644 --- a/test/syscalls/linux/mempolicy.cc +++ b/test/syscalls/linux/mempolicy.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mincore.cc b/test/syscalls/linux/mincore.cc index c572bf5ec..5c1240c89 100644 --- a/test/syscalls/linux/mincore.cc +++ b/test/syscalls/linux/mincore.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mkdir.cc b/test/syscalls/linux/mkdir.cc index 50807b68f..cf138d328 100644 --- a/test/syscalls/linux/mkdir.cc +++ b/test/syscalls/linux/mkdir.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mknod.cc b/test/syscalls/linux/mknod.cc index 361ca299b..b1675b9c7 100644 --- a/test/syscalls/linux/mknod.cc +++ b/test/syscalls/linux/mknod.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mlock.cc b/test/syscalls/linux/mlock.cc index a492b2404..aee4f7d1a 100644 --- a/test/syscalls/linux/mlock.cc +++ b/test/syscalls/linux/mlock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mmap.cc b/test/syscalls/linux/mmap.cc index a4fb9d1e0..5b5b4c2e8 100644 --- a/test/syscalls/linux/mmap.cc +++ b/test/syscalls/linux/mmap.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc index 201b83e87..3a17672aa 100644 --- a/test/syscalls/linux/mount.cc +++ b/test/syscalls/linux/mount.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/mremap.cc b/test/syscalls/linux/mremap.cc index 01116c1ab..7298d4ca8 100644 --- a/test/syscalls/linux/mremap.cc +++ b/test/syscalls/linux/mremap.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/msync.cc b/test/syscalls/linux/msync.cc index 5afbfce72..ac7146017 100644 --- a/test/syscalls/linux/msync.cc +++ b/test/syscalls/linux/msync.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/munmap.cc b/test/syscalls/linux/munmap.cc index e20039950..067241f4d 100644 --- a/test/syscalls/linux/munmap.cc +++ b/test/syscalls/linux/munmap.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc index 22e4666c2..42646bb02 100644 --- a/test/syscalls/linux/open.cc +++ b/test/syscalls/linux/open.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/open_create.cc b/test/syscalls/linux/open_create.cc index b2cbd63d1..e5a85ef9d 100644 --- a/test/syscalls/linux/open_create.cc +++ b/test/syscalls/linux/open_create.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/partial_bad_buffer.cc b/test/syscalls/linux/partial_bad_buffer.cc index 71288ebc4..83b1ad4e4 100644 --- a/test/syscalls/linux/partial_bad_buffer.cc +++ b/test/syscalls/linux/partial_bad_buffer.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/pause.cc b/test/syscalls/linux/pause.cc index 4e1148c24..8c05efd6f 100644 --- a/test/syscalls/linux/pause.cc +++ b/test/syscalls/linux/pause.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc index abd10b11b..8698295b3 100644 --- a/test/syscalls/linux/pipe.cc +++ b/test/syscalls/linux/pipe.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/poll.cc b/test/syscalls/linux/poll.cc index cd2161bb1..9e5aa7fd0 100644 --- a/test/syscalls/linux/poll.cc +++ b/test/syscalls/linux/poll.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/ppoll.cc b/test/syscalls/linux/ppoll.cc index f8c388c00..8245a11e8 100644 --- a/test/syscalls/linux/ppoll.cc +++ b/test/syscalls/linux/ppoll.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/prctl.cc b/test/syscalls/linux/prctl.cc index 854dec714..bce42dc74 100644 --- a/test/syscalls/linux/prctl.cc +++ b/test/syscalls/linux/prctl.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/prctl_setuid.cc b/test/syscalls/linux/prctl_setuid.cc index c1b561464..00dd6523e 100644 --- a/test/syscalls/linux/prctl_setuid.cc +++ b/test/syscalls/linux/prctl_setuid.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/pread64.cc b/test/syscalls/linux/pread64.cc index 4e5bcfcde..5e3eb1735 100644 --- a/test/syscalls/linux/pread64.cc +++ b/test/syscalls/linux/pread64.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/preadv.cc b/test/syscalls/linux/preadv.cc index 4a31123d8..eebd129f2 100644 --- a/test/syscalls/linux/preadv.cc +++ b/test/syscalls/linux/preadv.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/preadv2.cc b/test/syscalls/linux/preadv2.cc index 58a4f9224..aac960130 100644 --- a/test/syscalls/linux/preadv2.cc +++ b/test/syscalls/linux/preadv2.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/priority.cc b/test/syscalls/linux/priority.cc index 3906c7132..1d9bdfa70 100644 --- a/test/syscalls/linux/priority.cc +++ b/test/syscalls/linux/priority.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/priority_execve.cc b/test/syscalls/linux/priority_execve.cc index 5604bd3d0..5cb343bad 100644 --- a/test/syscalls/linux/priority_execve.cc +++ b/test/syscalls/linux/priority_execve.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc index 7ba274226..654f26242 100644 --- a/test/syscalls/linux/proc.cc +++ b/test/syscalls/linux/proc.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc index 6060d0644..03d0665eb 100644 --- a/test/syscalls/linux/proc_net.cc +++ b/test/syscalls/linux/proc_net.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/proc_net_unix.cc b/test/syscalls/linux/proc_net_unix.cc index ea7c93012..6d745f728 100644 --- a/test/syscalls/linux/proc_net_unix.cc +++ b/test/syscalls/linux/proc_net_unix.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/proc_pid_smaps.cc b/test/syscalls/linux/proc_pid_smaps.cc index cf5c462f3..7f2e8f203 100644 --- a/test/syscalls/linux/proc_pid_smaps.cc +++ b/test/syscalls/linux/proc_pid_smaps.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/proc_pid_uid_gid_map.cc b/test/syscalls/linux/proc_pid_uid_gid_map.cc index 96c58c564..df70b7eb9 100644 --- a/test/syscalls/linux/proc_pid_uid_gid_map.cc +++ b/test/syscalls/linux/proc_pid_uid_gid_map.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/pselect.cc b/test/syscalls/linux/pselect.cc index 3294f6c14..4e43c4d7f 100644 --- a/test/syscalls/linux/pselect.cc +++ b/test/syscalls/linux/pselect.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc index e0c56f1fc..4c212836c 100644 --- a/test/syscalls/linux/ptrace.cc +++ b/test/syscalls/linux/ptrace.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc index 5b2dc9ccb..0485d187c 100644 --- a/test/syscalls/linux/pty.cc +++ b/test/syscalls/linux/pty.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/pwrite64.cc b/test/syscalls/linux/pwrite64.cc index 485b1e48d..e1603fc2d 100644 --- a/test/syscalls/linux/pwrite64.cc +++ b/test/syscalls/linux/pwrite64.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/pwritev2.cc b/test/syscalls/linux/pwritev2.cc index a6949f08e..db519f4e0 100644 --- a/test/syscalls/linux/pwritev2.cc +++ b/test/syscalls/linux/pwritev2.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/raw_socket_ipv4.cc b/test/syscalls/linux/raw_socket_ipv4.cc index 8b8d032cb..e20b5cb50 100644 --- a/test/syscalls/linux/raw_socket_ipv4.cc +++ b/test/syscalls/linux/raw_socket_ipv4.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/read.cc b/test/syscalls/linux/read.cc index eb1b5bc10..4430fa3c2 100644 --- a/test/syscalls/linux/read.cc +++ b/test/syscalls/linux/read.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/readv.cc b/test/syscalls/linux/readv.cc index 0b933673a..f327ec3a9 100644 --- a/test/syscalls/linux/readv.cc +++ b/test/syscalls/linux/readv.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/readv_common.cc b/test/syscalls/linux/readv_common.cc index 349b80d7f..35d2dd9e3 100644 --- a/test/syscalls/linux/readv_common.cc +++ b/test/syscalls/linux/readv_common.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/readv_common.h b/test/syscalls/linux/readv_common.h index e261d545a..b16179fca 100644 --- a/test/syscalls/linux/readv_common.h +++ b/test/syscalls/linux/readv_common.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/readv_socket.cc b/test/syscalls/linux/readv_socket.cc index cf22c395e..3c315cc02 100644 --- a/test/syscalls/linux/readv_socket.cc +++ b/test/syscalls/linux/readv_socket.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/rename.cc b/test/syscalls/linux/rename.cc index c0cbc7cd9..c9d76c2e2 100644 --- a/test/syscalls/linux/rename.cc +++ b/test/syscalls/linux/rename.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/rlimits.cc b/test/syscalls/linux/rlimits.cc index 7b255d0f6..860f0f688 100644 --- a/test/syscalls/linux/rlimits.cc +++ b/test/syscalls/linux/rlimits.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/rtsignal.cc b/test/syscalls/linux/rtsignal.cc index ff948f9d5..81d193ffd 100644 --- a/test/syscalls/linux/rtsignal.cc +++ b/test/syscalls/linux/rtsignal.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sched.cc b/test/syscalls/linux/sched.cc index 60cb6c443..735e99411 100644 --- a/test/syscalls/linux/sched.cc +++ b/test/syscalls/linux/sched.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sched_yield.cc b/test/syscalls/linux/sched_yield.cc index fc45aa5c2..5d24f5b58 100644 --- a/test/syscalls/linux/sched_yield.cc +++ b/test/syscalls/linux/sched_yield.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/seccomp.cc b/test/syscalls/linux/seccomp.cc index 27740d7ef..e77586852 100644 --- a/test/syscalls/linux/seccomp.cc +++ b/test/syscalls/linux/seccomp.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/select.cc b/test/syscalls/linux/select.cc index 41e6043cc..88c010aec 100644 --- a/test/syscalls/linux/select.cc +++ b/test/syscalls/linux/select.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc index 1c47b6851..421318fcb 100644 --- a/test/syscalls/linux/semaphore.cc +++ b/test/syscalls/linux/semaphore.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sendfile.cc b/test/syscalls/linux/sendfile.cc index 15fd01ff0..2fbb3f4ef 100644 --- a/test/syscalls/linux/sendfile.cc +++ b/test/syscalls/linux/sendfile.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sendfile_socket.cc b/test/syscalls/linux/sendfile_socket.cc index e2ccf17ce..66adda515 100644 --- a/test/syscalls/linux/sendfile_socket.cc +++ b/test/syscalls/linux/sendfile_socket.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/shm.cc b/test/syscalls/linux/shm.cc index 2c0f9b04a..eb7a3966f 100644 --- a/test/syscalls/linux/shm.cc +++ b/test/syscalls/linux/shm.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sigaction.cc b/test/syscalls/linux/sigaction.cc index cdd2dbf31..9a53fd3e0 100644 --- a/test/syscalls/linux/sigaction.cc +++ b/test/syscalls/linux/sigaction.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sigaltstack.cc b/test/syscalls/linux/sigaltstack.cc index 5741720f4..7d4a12c1d 100644 --- a/test/syscalls/linux/sigaltstack.cc +++ b/test/syscalls/linux/sigaltstack.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sigaltstack_check.cc b/test/syscalls/linux/sigaltstack_check.cc index b71f812a8..5ac1b661d 100644 --- a/test/syscalls/linux/sigaltstack_check.cc +++ b/test/syscalls/linux/sigaltstack_check.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sigiret.cc b/test/syscalls/linux/sigiret.cc index 1b7cecccb..a47c781ea 100644 --- a/test/syscalls/linux/sigiret.cc +++ b/test/syscalls/linux/sigiret.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sigprocmask.cc b/test/syscalls/linux/sigprocmask.cc index 1aea1ecb8..654c6a47f 100644 --- a/test/syscalls/linux/sigprocmask.cc +++ b/test/syscalls/linux/sigprocmask.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sigstop.cc b/test/syscalls/linux/sigstop.cc index e21d23d51..9c7210e17 100644 --- a/test/syscalls/linux/sigstop.cc +++ b/test/syscalls/linux/sigstop.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sigtimedwait.cc b/test/syscalls/linux/sigtimedwait.cc index 1df9c013f..1e5bf5942 100644 --- a/test/syscalls/linux/sigtimedwait.cc +++ b/test/syscalls/linux/sigtimedwait.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_abstract.cc b/test/syscalls/linux/socket_abstract.cc index 639cd4e59..2faf678f7 100644 --- a/test/syscalls/linux/socket_abstract.cc +++ b/test/syscalls/linux/socket_abstract.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_blocking.cc b/test/syscalls/linux/socket_blocking.cc index c1bca467f..00c50d1bf 100644 --- a/test/syscalls/linux/socket_blocking.cc +++ b/test/syscalls/linux/socket_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_blocking.h b/test/syscalls/linux/socket_blocking.h index 5cddee54b..db26e5ef5 100644 --- a/test/syscalls/linux/socket_blocking.h +++ b/test/syscalls/linux/socket_blocking.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_filesystem.cc b/test/syscalls/linux/socket_filesystem.cc index 2653be158..f7cb72df4 100644 --- a/test/syscalls/linux/socket_filesystem.cc +++ b/test/syscalls/linux/socket_filesystem.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_generic.cc b/test/syscalls/linux/socket_generic.cc index d04d5abe0..f99f3fe62 100644 --- a/test/syscalls/linux/socket_generic.cc +++ b/test/syscalls/linux/socket_generic.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_generic.h b/test/syscalls/linux/socket_generic.h index cd826abcf..00ae7bfc3 100644 --- a/test/syscalls/linux/socket_generic.h +++ b/test/syscalls/linux/socket_generic.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc index 14d7827c2..f86a0f30c 100644 --- a/test/syscalls/linux/socket_inet_loopback.cc +++ b/test/syscalls/linux/socket_inet_loopback.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_loopback_blocking.cc b/test/syscalls/linux/socket_ip_loopback_blocking.cc index 9cec7a71d..d7fc20aad 100644 --- a/test/syscalls/linux/socket_ip_loopback_blocking.cc +++ b/test/syscalls/linux/socket_ip_loopback_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc index 54f00cd9b..5b198f49d 100644 --- a/test/syscalls/linux/socket_ip_tcp_generic.cc +++ b/test/syscalls/linux/socket_ip_tcp_generic.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_tcp_generic.h b/test/syscalls/linux/socket_ip_tcp_generic.h index f38500d14..a3eff3c73 100644 --- a/test/syscalls/linux/socket_ip_tcp_generic.h +++ b/test/syscalls/linux/socket_ip_tcp_generic.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc index 1963d5deb..2c6ae17bf 100644 --- a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc +++ b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_tcp_loopback.cc b/test/syscalls/linux/socket_ip_tcp_loopback.cc index 7e36c35d2..831de53b8 100644 --- a/test/syscalls/linux/socket_ip_tcp_loopback.cc +++ b/test/syscalls/linux/socket_ip_tcp_loopback.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc index 9e2a18d3e..d1ea8ef12 100644 --- a/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc +++ b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc b/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc index 54053360f..96c1b3b3d 100644 --- a/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc +++ b/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_tcp_udp_generic.cc b/test/syscalls/linux/socket_ip_tcp_udp_generic.cc index 5bf1de7c6..251817a9f 100644 --- a/test/syscalls/linux/socket_ip_tcp_udp_generic.cc +++ b/test/syscalls/linux/socket_ip_tcp_udp_generic.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc index ac15154f2..044394ba7 100644 --- a/test/syscalls/linux/socket_ip_udp_generic.cc +++ b/test/syscalls/linux/socket_ip_udp_generic.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_udp_generic.h b/test/syscalls/linux/socket_ip_udp_generic.h index 8b8fc7c6e..106c54e9f 100644 --- a/test/syscalls/linux/socket_ip_udp_generic.h +++ b/test/syscalls/linux/socket_ip_udp_generic.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_udp_loopback.cc b/test/syscalls/linux/socket_ip_udp_loopback.cc index 0e4463649..fc124e9ef 100644 --- a/test/syscalls/linux/socket_ip_udp_loopback.cc +++ b/test/syscalls/linux/socket_ip_udp_loopback.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc b/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc index 0c3b669bf..1c3d1c0ad 100644 --- a/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc +++ b/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc b/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc index 7bf8597fe..7554b08d5 100644 --- a/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc +++ b/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc index 8e1c13ff4..3a068aacf 100644 --- a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc +++ b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h index b23de08d1..fb582b224 100644 --- a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h +++ b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc index 773d84b13..040bb176e 100644 --- a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc +++ b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc index c99958ed5..709172580 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.h b/test/syscalls/linux/socket_ipv4_udp_unbound.h index a780c0144..8e07bfbbf 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound.h +++ b/test/syscalls/linux/socket_ipv4_udp_unbound.h @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc index 9dd9e1bd6..53dcd58cd 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h index 5cf9fa8eb..45e1d37ea 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc index 535a5fa10..ffbb8e6eb 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc index d6a8e428c..cb0105471 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc index b4e9fe51b..6a5fa8965 100644 --- a/test/syscalls/linux/socket_netdevice.cc +++ b/test/syscalls/linux/socket_netdevice.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index ed4ae1c71..c8693225f 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc index edf549544..728d25434 100644 --- a/test/syscalls/linux/socket_netlink_util.cc +++ b/test/syscalls/linux/socket_netlink_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_netlink_util.h b/test/syscalls/linux/socket_netlink_util.h index 44b1f148c..bea449107 100644 --- a/test/syscalls/linux/socket_netlink_util.h +++ b/test/syscalls/linux/socket_netlink_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_non_blocking.cc b/test/syscalls/linux/socket_non_blocking.cc index 1bcc6fb7f..73e6dc618 100644 --- a/test/syscalls/linux/socket_non_blocking.cc +++ b/test/syscalls/linux/socket_non_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_non_blocking.h b/test/syscalls/linux/socket_non_blocking.h index 287e096bb..bd3e02fd2 100644 --- a/test/syscalls/linux/socket_non_blocking.h +++ b/test/syscalls/linux/socket_non_blocking.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_non_stream.cc b/test/syscalls/linux/socket_non_stream.cc index d170008a4..3c599b6e8 100644 --- a/test/syscalls/linux/socket_non_stream.cc +++ b/test/syscalls/linux/socket_non_stream.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_non_stream.h b/test/syscalls/linux/socket_non_stream.h index 02dd2a958..469fbe6a2 100644 --- a/test/syscalls/linux/socket_non_stream.h +++ b/test/syscalls/linux/socket_non_stream.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_non_stream_blocking.cc b/test/syscalls/linux/socket_non_stream_blocking.cc index 9e92628c3..76127d181 100644 --- a/test/syscalls/linux/socket_non_stream_blocking.cc +++ b/test/syscalls/linux/socket_non_stream_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_non_stream_blocking.h b/test/syscalls/linux/socket_non_stream_blocking.h index bde355452..6e205a039 100644 --- a/test/syscalls/linux/socket_non_stream_blocking.h +++ b/test/syscalls/linux/socket_non_stream_blocking.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_stream.cc b/test/syscalls/linux/socket_stream.cc index c8a8ad0f6..0417dd347 100644 --- a/test/syscalls/linux/socket_stream.cc +++ b/test/syscalls/linux/socket_stream.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_stream.h b/test/syscalls/linux/socket_stream.h index 35e591e17..b837b8f8c 100644 --- a/test/syscalls/linux/socket_stream.h +++ b/test/syscalls/linux/socket_stream.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_stream_blocking.cc b/test/syscalls/linux/socket_stream_blocking.cc index f0f86c01c..8367460d2 100644 --- a/test/syscalls/linux/socket_stream_blocking.cc +++ b/test/syscalls/linux/socket_stream_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_stream_blocking.h b/test/syscalls/linux/socket_stream_blocking.h index 06113ad03..9fd19ff90 100644 --- a/test/syscalls/linux/socket_stream_blocking.h +++ b/test/syscalls/linux/socket_stream_blocking.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_stream_nonblock.cc b/test/syscalls/linux/socket_stream_nonblock.cc index a3202ffe4..b00748b97 100644 --- a/test/syscalls/linux/socket_stream_nonblock.cc +++ b/test/syscalls/linux/socket_stream_nonblock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_stream_nonblock.h b/test/syscalls/linux/socket_stream_nonblock.h index 491f53848..c3b7fad91 100644 --- a/test/syscalls/linux/socket_stream_nonblock.h +++ b/test/syscalls/linux/socket_stream_nonblock.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index 0be23e541..da69de37c 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h index dfabdf179..058313986 100644 --- a/test/syscalls/linux/socket_test_util.h +++ b/test/syscalls/linux/socket_test_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix.cc b/test/syscalls/linux/socket_unix.cc index fafb23ad1..bb3397fa2 100644 --- a/test/syscalls/linux/socket_unix.cc +++ b/test/syscalls/linux/socket_unix.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix.h b/test/syscalls/linux/socket_unix.h index d2a16afb2..3625cc404 100644 --- a/test/syscalls/linux/socket_unix.h +++ b/test/syscalls/linux/socket_unix.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_abstract.cc b/test/syscalls/linux/socket_unix_abstract.cc index c4a3c889c..8241bf997 100644 --- a/test/syscalls/linux/socket_unix_abstract.cc +++ b/test/syscalls/linux/socket_unix_abstract.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_abstract_nonblock.cc b/test/syscalls/linux/socket_unix_abstract_nonblock.cc index a69ee027e..9de0f6dfe 100644 --- a/test/syscalls/linux/socket_unix_abstract_nonblock.cc +++ b/test/syscalls/linux/socket_unix_abstract_nonblock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_blocking_local.cc b/test/syscalls/linux/socket_unix_blocking_local.cc index 57af118c5..320915b0f 100644 --- a/test/syscalls/linux/socket_unix_blocking_local.cc +++ b/test/syscalls/linux/socket_unix_blocking_local.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_dgram.cc b/test/syscalls/linux/socket_unix_dgram.cc index 5dd5e6d77..3e0f611d2 100644 --- a/test/syscalls/linux/socket_unix_dgram.cc +++ b/test/syscalls/linux/socket_unix_dgram.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_dgram.h b/test/syscalls/linux/socket_unix_dgram.h index 722a3d8e6..0764ef85b 100644 --- a/test/syscalls/linux/socket_unix_dgram.h +++ b/test/syscalls/linux/socket_unix_dgram.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_dgram_local.cc b/test/syscalls/linux/socket_unix_dgram_local.cc index da8f59704..4ba2c80ae 100644 --- a/test/syscalls/linux/socket_unix_dgram_local.cc +++ b/test/syscalls/linux/socket_unix_dgram_local.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc index 3becb513d..9fe86cee8 100644 --- a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc +++ b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_domain.cc b/test/syscalls/linux/socket_unix_domain.cc index f081c601f..fa3efc7f8 100644 --- a/test/syscalls/linux/socket_unix_domain.cc +++ b/test/syscalls/linux/socket_unix_domain.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_filesystem.cc b/test/syscalls/linux/socket_unix_filesystem.cc index 6a67da75f..5dbe67773 100644 --- a/test/syscalls/linux/socket_unix_filesystem.cc +++ b/test/syscalls/linux/socket_unix_filesystem.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc index c13a1e564..137db53c4 100644 --- a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc +++ b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_non_stream.cc b/test/syscalls/linux/socket_unix_non_stream.cc index a565978f9..dafe82494 100644 --- a/test/syscalls/linux/socket_unix_non_stream.cc +++ b/test/syscalls/linux/socket_unix_non_stream.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_non_stream.h b/test/syscalls/linux/socket_unix_non_stream.h index e4214d949..7478ab172 100644 --- a/test/syscalls/linux/socket_unix_non_stream.h +++ b/test/syscalls/linux/socket_unix_non_stream.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc index 6c435669b..98cf1fe8a 100644 --- a/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc +++ b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_pair.cc b/test/syscalls/linux/socket_unix_pair.cc index c575fdcb2..bacfc11e4 100644 --- a/test/syscalls/linux/socket_unix_pair.cc +++ b/test/syscalls/linux/socket_unix_pair.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_pair_nonblock.cc b/test/syscalls/linux/socket_unix_pair_nonblock.cc index 1ae7f9b5e..583506f08 100644 --- a/test/syscalls/linux/socket_unix_pair_nonblock.cc +++ b/test/syscalls/linux/socket_unix_pair_nonblock.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_seqpacket.cc b/test/syscalls/linux/socket_unix_seqpacket.cc index ad0af77e9..6f6367dd5 100644 --- a/test/syscalls/linux/socket_unix_seqpacket.cc +++ b/test/syscalls/linux/socket_unix_seqpacket.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_seqpacket.h b/test/syscalls/linux/socket_unix_seqpacket.h index da8eb2b2b..30d9b9edf 100644 --- a/test/syscalls/linux/socket_unix_seqpacket.h +++ b/test/syscalls/linux/socket_unix_seqpacket.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_seqpacket_local.cc b/test/syscalls/linux/socket_unix_seqpacket_local.cc index e6484d9b4..b903a9e8f 100644 --- a/test/syscalls/linux/socket_unix_seqpacket_local.cc +++ b/test/syscalls/linux/socket_unix_seqpacket_local.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_stream.cc b/test/syscalls/linux/socket_unix_stream.cc index 95f454251..659c93945 100644 --- a/test/syscalls/linux/socket_unix_stream.cc +++ b/test/syscalls/linux/socket_unix_stream.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_stream_blocking_local.cc b/test/syscalls/linux/socket_unix_stream_blocking_local.cc index ec0fc6955..ce0f1e50d 100644 --- a/test/syscalls/linux/socket_unix_stream_blocking_local.cc +++ b/test/syscalls/linux/socket_unix_stream_blocking_local.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_stream_local.cc b/test/syscalls/linux/socket_unix_stream_local.cc index bf4c5f2eb..6b840189c 100644 --- a/test/syscalls/linux/socket_unix_stream_local.cc +++ b/test/syscalls/linux/socket_unix_stream_local.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_stream_nonblock_local.cc b/test/syscalls/linux/socket_unix_stream_nonblock_local.cc index df80b105a..ebec4e0ec 100644 --- a/test/syscalls/linux/socket_unix_stream_nonblock_local.cc +++ b/test/syscalls/linux/socket_unix_stream_nonblock_local.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_unbound_abstract.cc b/test/syscalls/linux/socket_unix_unbound_abstract.cc index b6fe7a9ce..4b5832de8 100644 --- a/test/syscalls/linux/socket_unix_unbound_abstract.cc +++ b/test/syscalls/linux/socket_unix_unbound_abstract.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_unbound_dgram.cc b/test/syscalls/linux/socket_unix_unbound_dgram.cc index 1ec11a08d..2ddc5c11f 100644 --- a/test/syscalls/linux/socket_unix_unbound_dgram.cc +++ b/test/syscalls/linux/socket_unix_unbound_dgram.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_unbound_filesystem.cc b/test/syscalls/linux/socket_unix_unbound_filesystem.cc index d09142aa6..8cb03c450 100644 --- a/test/syscalls/linux/socket_unix_unbound_filesystem.cc +++ b/test/syscalls/linux/socket_unix_unbound_filesystem.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc index 21209b244..0575f2e1d 100644 --- a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc +++ b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/socket_unix_unbound_stream.cc b/test/syscalls/linux/socket_unix_unbound_stream.cc index b95f9569e..091d546b3 100644 --- a/test/syscalls/linux/socket_unix_unbound_stream.cc +++ b/test/syscalls/linux/socket_unix_unbound_stream.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc index 746318d09..80ba67496 100644 --- a/test/syscalls/linux/stat.cc +++ b/test/syscalls/linux/stat.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/stat_times.cc b/test/syscalls/linux/stat_times.cc index 8346e9a8e..9b53739a0 100644 --- a/test/syscalls/linux/stat_times.cc +++ b/test/syscalls/linux/stat_times.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/statfs.cc b/test/syscalls/linux/statfs.cc index e1e7fc707..aca51d30f 100644 --- a/test/syscalls/linux/statfs.cc +++ b/test/syscalls/linux/statfs.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sticky.cc b/test/syscalls/linux/sticky.cc index 58cf0d014..59fb5dfe6 100644 --- a/test/syscalls/linux/sticky.cc +++ b/test/syscalls/linux/sticky.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/symlink.cc b/test/syscalls/linux/symlink.cc index 318917f4b..494072a9b 100644 --- a/test/syscalls/linux/symlink.cc +++ b/test/syscalls/linux/symlink.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sync.cc b/test/syscalls/linux/sync.cc index 5b777b6eb..fe479390d 100644 --- a/test/syscalls/linux/sync.cc +++ b/test/syscalls/linux/sync.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sync_file_range.cc b/test/syscalls/linux/sync_file_range.cc index d11f58481..36cc42043 100644 --- a/test/syscalls/linux/sync_file_range.cc +++ b/test/syscalls/linux/sync_file_range.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sysinfo.cc b/test/syscalls/linux/sysinfo.cc index a0dd82640..1a71256da 100644 --- a/test/syscalls/linux/sysinfo.cc +++ b/test/syscalls/linux/sysinfo.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/syslog.cc b/test/syscalls/linux/syslog.cc index 5bd0d1cc3..9a7407d96 100644 --- a/test/syscalls/linux/syslog.cc +++ b/test/syscalls/linux/syslog.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/sysret.cc b/test/syscalls/linux/sysret.cc index 8e10220eb..819fa655a 100644 --- a/test/syscalls/linux/sysret.cc +++ b/test/syscalls/linux/sysret.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 33620a874..e3f9f9f9d 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/temp_umask.h b/test/syscalls/linux/temp_umask.h index f202dfa59..81a25440c 100644 --- a/test/syscalls/linux/temp_umask.h +++ b/test/syscalls/linux/temp_umask.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/tgkill.cc b/test/syscalls/linux/tgkill.cc index 2d258ef11..80acae5de 100644 --- a/test/syscalls/linux/tgkill.cc +++ b/test/syscalls/linux/tgkill.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/time.cc b/test/syscalls/linux/time.cc index 5a3dfd026..c7eead17e 100644 --- a/test/syscalls/linux/time.cc +++ b/test/syscalls/linux/time.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/timerfd.cc b/test/syscalls/linux/timerfd.cc index b85321795..9df53612f 100644 --- a/test/syscalls/linux/timerfd.cc +++ b/test/syscalls/linux/timerfd.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/timers.cc b/test/syscalls/linux/timers.cc index 14506eb12..fd42e81e1 100644 --- a/test/syscalls/linux/timers.cc +++ b/test/syscalls/linux/timers.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/tkill.cc b/test/syscalls/linux/tkill.cc index 3e8ce5327..bae377c69 100644 --- a/test/syscalls/linux/tkill.cc +++ b/test/syscalls/linux/tkill.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/truncate.cc b/test/syscalls/linux/truncate.cc index 2616a9147..e5cc5d97c 100644 --- a/test/syscalls/linux/truncate.cc +++ b/test/syscalls/linux/truncate.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/udp_bind.cc b/test/syscalls/linux/udp_bind.cc index 547eb2a6c..6d92bdbeb 100644 --- a/test/syscalls/linux/udp_bind.cc +++ b/test/syscalls/linux/udp_bind.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc index f39281d5c..31db8a2ad 100644 --- a/test/syscalls/linux/udp_socket.cc +++ b/test/syscalls/linux/udp_socket.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/uidgid.cc b/test/syscalls/linux/uidgid.cc index d78a09b1e..bf1ca8679 100644 --- a/test/syscalls/linux/uidgid.cc +++ b/test/syscalls/linux/uidgid.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/uname.cc b/test/syscalls/linux/uname.cc index d22a34bd7..0a5d91017 100644 --- a/test/syscalls/linux/uname.cc +++ b/test/syscalls/linux/uname.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/unix_domain_socket_test_util.cc b/test/syscalls/linux/unix_domain_socket_test_util.cc index 2d7a530b9..6f49e3660 100644 --- a/test/syscalls/linux/unix_domain_socket_test_util.cc +++ b/test/syscalls/linux/unix_domain_socket_test_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/unix_domain_socket_test_util.h b/test/syscalls/linux/unix_domain_socket_test_util.h index 1b09aeae7..aae990245 100644 --- a/test/syscalls/linux/unix_domain_socket_test_util.h +++ b/test/syscalls/linux/unix_domain_socket_test_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/unlink.cc b/test/syscalls/linux/unlink.cc index b10aae025..b6f65e027 100644 --- a/test/syscalls/linux/unlink.cc +++ b/test/syscalls/linux/unlink.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/unshare.cc b/test/syscalls/linux/unshare.cc index 9dd6ec4b6..e32619efe 100644 --- a/test/syscalls/linux/unshare.cc +++ b/test/syscalls/linux/unshare.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/utimes.cc b/test/syscalls/linux/utimes.cc index bf776cd93..80716859a 100644 --- a/test/syscalls/linux/utimes.cc +++ b/test/syscalls/linux/utimes.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/vdso.cc b/test/syscalls/linux/vdso.cc index 0f6e1c7c6..19c80add8 100644 --- a/test/syscalls/linux/vdso.cc +++ b/test/syscalls/linux/vdso.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/vdso_clock_gettime.cc b/test/syscalls/linux/vdso_clock_gettime.cc index 0e936594b..759a50569 100644 --- a/test/syscalls/linux/vdso_clock_gettime.cc +++ b/test/syscalls/linux/vdso_clock_gettime.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/vfork.cc b/test/syscalls/linux/vfork.cc index 9999a909e..631a53654 100644 --- a/test/syscalls/linux/vfork.cc +++ b/test/syscalls/linux/vfork.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/vsyscall.cc b/test/syscalls/linux/vsyscall.cc index cb6840cc6..2c2303358 100644 --- a/test/syscalls/linux/vsyscall.cc +++ b/test/syscalls/linux/vsyscall.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/wait.cc b/test/syscalls/linux/wait.cc index fcd606bec..50d0725a7 100644 --- a/test/syscalls/linux/wait.cc +++ b/test/syscalls/linux/wait.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/linux/write.cc b/test/syscalls/linux/write.cc index 7f80b2fa8..9b219cfd6 100644 --- a/test/syscalls/linux/write.cc +++ b/test/syscalls/linux/write.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/syscall_test_runner.go b/test/syscalls/syscall_test_runner.go index c4af28103..28f312b8b 100644 --- a/test/syscalls/syscall_test_runner.go +++ b/test/syscalls/syscall_test_runner.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/syscalls/syscall_test_runner.sh b/test/syscalls/syscall_test_runner.sh index 87d62786b..864bb2de4 100755 --- a/test/syscalls/syscall_test_runner.sh +++ b/test/syscalls/syscall_test_runner.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc index d1dd95e76..5d733887b 100644 --- a/test/util/capability_util.cc +++ b/test/util/capability_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/capability_util.h b/test/util/capability_util.h index 8708f5e69..e968a2583 100644 --- a/test/util/capability_util.h +++ b/test/util/capability_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/cleanup.h b/test/util/cleanup.h index fb4724f97..c76482ef4 100644 --- a/test/util/cleanup.h +++ b/test/util/cleanup.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/epoll_util.cc b/test/util/epoll_util.cc index 0b95aa8cd..2e5051468 100644 --- a/test/util/epoll_util.cc +++ b/test/util/epoll_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/epoll_util.h b/test/util/epoll_util.h index 521e7a3d3..f233b37d5 100644 --- a/test/util/epoll_util.h +++ b/test/util/epoll_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/eventfd_util.h b/test/util/eventfd_util.h index 1fdb07d3b..cb9ce829c 100644 --- a/test/util/eventfd_util.h +++ b/test/util/eventfd_util.h @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/util/file_descriptor.h b/test/util/file_descriptor.h index be8812d01..fc5caa55b 100644 --- a/test/util/file_descriptor.h +++ b/test/util/file_descriptor.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc index 6bd424417..bc90bd78e 100644 --- a/test/util/fs_util.cc +++ b/test/util/fs_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/fs_util.h b/test/util/fs_util.h index 9412b2f71..eb7cdaa24 100644 --- a/test/util/fs_util.h +++ b/test/util/fs_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/fs_util_test.cc b/test/util/fs_util_test.cc index ce70d58aa..4e12076a1 100644 --- a/test/util/fs_util_test.cc +++ b/test/util/fs_util_test.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/logging.cc b/test/util/logging.cc index 86ea71df3..cc71d77b0 100644 --- a/test/util/logging.cc +++ b/test/util/logging.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/logging.h b/test/util/logging.h index 6e957b172..589166fab 100644 --- a/test/util/logging.h +++ b/test/util/logging.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/memory_util.h b/test/util/memory_util.h index 8f6e99ba6..8c77778ea 100644 --- a/test/util/memory_util.h +++ b/test/util/memory_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/mount_util.h b/test/util/mount_util.h index 468170646..7782e6bf2 100644 --- a/test/util/mount_util.h +++ b/test/util/mount_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/multiprocess_util.cc b/test/util/multiprocess_util.cc index 12637db8c..95f5f3b4f 100644 --- a/test/util/multiprocess_util.cc +++ b/test/util/multiprocess_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/multiprocess_util.h b/test/util/multiprocess_util.h index ba5f2601f..0aecd3439 100644 --- a/test/util/multiprocess_util.h +++ b/test/util/multiprocess_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/posix_error.cc b/test/util/posix_error.cc index ead9ede16..cebf7e0ac 100644 --- a/test/util/posix_error.cc +++ b/test/util/posix_error.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/posix_error.h b/test/util/posix_error.h index 2a66e2e94..b604f4f8f 100644 --- a/test/util/posix_error.h +++ b/test/util/posix_error.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/posix_error_test.cc b/test/util/posix_error_test.cc index c5427b8e5..d67270842 100644 --- a/test/util/posix_error_test.cc +++ b/test/util/posix_error_test.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/proc_util.cc b/test/util/proc_util.cc index 2d9eb1986..9d4db37c3 100644 --- a/test/util/proc_util.cc +++ b/test/util/proc_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/proc_util.h b/test/util/proc_util.h index e1ee2db9c..af209a51e 100644 --- a/test/util/proc_util.h +++ b/test/util/proc_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/proc_util_test.cc b/test/util/proc_util_test.cc index 75335415a..71dd2355e 100644 --- a/test/util/proc_util_test.cc +++ b/test/util/proc_util_test.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/rlimit_util.cc b/test/util/rlimit_util.cc index a9912c372..684253f78 100644 --- a/test/util/rlimit_util.cc +++ b/test/util/rlimit_util.cc @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/util/rlimit_util.h b/test/util/rlimit_util.h index fa5cc70dc..873252a32 100644 --- a/test/util/rlimit_util.h +++ b/test/util/rlimit_util.h @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// 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. diff --git a/test/util/save_util.cc b/test/util/save_util.cc index 5540e2146..05f52b80d 100644 --- a/test/util/save_util.cc +++ b/test/util/save_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/save_util.h b/test/util/save_util.h index 919e4af3d..90460701e 100644 --- a/test/util/save_util.h +++ b/test/util/save_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/signal_util.cc b/test/util/signal_util.cc index 3e2df32a6..26738864f 100644 --- a/test/util/signal_util.cc +++ b/test/util/signal_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/signal_util.h b/test/util/signal_util.h index 80f1808f6..7fd2af015 100644 --- a/test/util/signal_util.h +++ b/test/util/signal_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/temp_path.cc b/test/util/temp_path.cc index 48ce82d20..c5d8fc635 100644 --- a/test/util/temp_path.cc +++ b/test/util/temp_path.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/temp_path.h b/test/util/temp_path.h index 33eb6a72c..89302e0fd 100644 --- a/test/util/temp_path.h +++ b/test/util/temp_path.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/test_main.cc b/test/util/test_main.cc index 4c6b5e860..5c7ee0064 100644 --- a/test/util/test_main.cc +++ b/test/util/test_main.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/test_util.cc b/test/util/test_util.cc index 9b7cfa4dc..c52fd9a4a 100644 --- a/test/util/test_util.cc +++ b/test/util/test_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/test_util.h b/test/util/test_util.h index 905412b24..8f5eb5089 100644 --- a/test/util/test_util.h +++ b/test/util/test_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/test_util_test.cc b/test/util/test_util_test.cc index 5889651d1..b7300d9e5 100644 --- a/test/util/test_util_test.cc +++ b/test/util/test_util_test.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/thread_util.h b/test/util/thread_util.h index df09ac8cf..860e77531 100644 --- a/test/util/thread_util.h +++ b/test/util/thread_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/timer_util.cc b/test/util/timer_util.cc index 681fafb69..43a26b0d3 100644 --- a/test/util/timer_util.cc +++ b/test/util/timer_util.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/test/util/timer_util.h b/test/util/timer_util.h index 9bdc51a57..2cebfa5d1 100644 --- a/test/util/timer_util.h +++ b/test/util/timer_util.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/third_party/gvsync/atomicptr_unsafe.go b/third_party/gvsync/atomicptr_unsafe.go index da9f16240..53a943282 100644 --- a/third_party/gvsync/atomicptr_unsafe.go +++ b/third_party/gvsync/atomicptr_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/atomicptrtest/atomicptr_test.go b/third_party/gvsync/atomicptrtest/atomicptr_test.go index 15d0936d4..8fdc5112e 100644 --- a/third_party/gvsync/atomicptrtest/atomicptr_test.go +++ b/third_party/gvsync/atomicptrtest/atomicptr_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/downgradable_rwmutex_test.go b/third_party/gvsync/downgradable_rwmutex_test.go index 6517dd5dc..40c384b8b 100644 --- a/third_party/gvsync/downgradable_rwmutex_test.go +++ b/third_party/gvsync/downgradable_rwmutex_test.go @@ -1,5 +1,5 @@ // Copyright 2009 The Go Authors. All rights reserved. -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/downgradable_rwmutex_unsafe.go b/third_party/gvsync/downgradable_rwmutex_unsafe.go index 131f0a2ba..4d43eb765 100644 --- a/third_party/gvsync/downgradable_rwmutex_unsafe.go +++ b/third_party/gvsync/downgradable_rwmutex_unsafe.go @@ -1,5 +1,5 @@ // Copyright 2009 The Go Authors. All rights reserved. -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/gvsync.go b/third_party/gvsync/gvsync.go index 46a2565fd..3bbef13c3 100644 --- a/third_party/gvsync/gvsync.go +++ b/third_party/gvsync/gvsync.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/memmove_unsafe.go b/third_party/gvsync/memmove_unsafe.go index d483fc739..4c8aa9ab6 100644 --- a/third_party/gvsync/memmove_unsafe.go +++ b/third_party/gvsync/memmove_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/norace_unsafe.go b/third_party/gvsync/norace_unsafe.go index f9c88d13f..e3852db8c 100644 --- a/third_party/gvsync/norace_unsafe.go +++ b/third_party/gvsync/norace_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/race_unsafe.go b/third_party/gvsync/race_unsafe.go index 2cdcdf7f7..13c02a830 100644 --- a/third_party/gvsync/race_unsafe.go +++ b/third_party/gvsync/race_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/seqatomic_unsafe.go b/third_party/gvsync/seqatomic_unsafe.go index ef61503e2..c52d378f1 100644 --- a/third_party/gvsync/seqatomic_unsafe.go +++ b/third_party/gvsync/seqatomic_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/seqatomictest/seqatomic_test.go b/third_party/gvsync/seqatomictest/seqatomic_test.go index d0c373bae..2da73cf96 100644 --- a/third_party/gvsync/seqatomictest/seqatomic_test.go +++ b/third_party/gvsync/seqatomictest/seqatomic_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/third_party/gvsync/seqcount.go b/third_party/gvsync/seqcount.go index c7ae91cfa..2c9c2c3d6 100644 --- a/third_party/gvsync/seqcount.go +++ b/third_party/gvsync/seqcount.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/third_party/gvsync/seqcount_test.go b/third_party/gvsync/seqcount_test.go index ee6579ed8..085e574b3 100644 --- a/third_party/gvsync/seqcount_test.go +++ b/third_party/gvsync/seqcount_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019 The gVisor Authors. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/tools/go_generics/generics.go b/tools/go_generics/generics.go index eaf5c4970..ca414d8cb 100644 --- a/tools/go_generics/generics.go +++ b/tools/go_generics/generics.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/all_stmts/input.go b/tools/go_generics/generics_tests/all_stmts/input.go index 19184a3fe..4791d1ff1 100644 --- a/tools/go_generics/generics_tests/all_stmts/input.go +++ b/tools/go_generics/generics_tests/all_stmts/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/all_stmts/output/output.go b/tools/go_generics/generics_tests/all_stmts/output/output.go index 51582346c..a53d84535 100644 --- a/tools/go_generics/generics_tests/all_stmts/output/output.go +++ b/tools/go_generics/generics_tests/all_stmts/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/all_types/input.go b/tools/go_generics/generics_tests/all_types/input.go index ed6e97c29..3575d02ec 100644 --- a/tools/go_generics/generics_tests/all_types/input.go +++ b/tools/go_generics/generics_tests/all_types/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/all_types/lib/lib.go b/tools/go_generics/generics_tests/all_types/lib/lib.go index 7e73e678e..988786496 100644 --- a/tools/go_generics/generics_tests/all_types/lib/lib.go +++ b/tools/go_generics/generics_tests/all_types/lib/lib.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/all_types/output/output.go b/tools/go_generics/generics_tests/all_types/output/output.go index ec09a6be4..41fd147a1 100644 --- a/tools/go_generics/generics_tests/all_types/output/output.go +++ b/tools/go_generics/generics_tests/all_types/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/consts/input.go b/tools/go_generics/generics_tests/consts/input.go index 394bcc262..04b95fcc6 100644 --- a/tools/go_generics/generics_tests/consts/input.go +++ b/tools/go_generics/generics_tests/consts/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/consts/output/output.go b/tools/go_generics/generics_tests/consts/output/output.go index 91a07fdc2..18d316cc9 100644 --- a/tools/go_generics/generics_tests/consts/output/output.go +++ b/tools/go_generics/generics_tests/consts/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/imports/input.go b/tools/go_generics/generics_tests/imports/input.go index 22e6641a6..0f032c2a1 100644 --- a/tools/go_generics/generics_tests/imports/input.go +++ b/tools/go_generics/generics_tests/imports/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/imports/output/output.go b/tools/go_generics/generics_tests/imports/output/output.go index 2555c0004..2488ca58c 100644 --- a/tools/go_generics/generics_tests/imports/output/output.go +++ b/tools/go_generics/generics_tests/imports/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/remove_typedef/input.go b/tools/go_generics/generics_tests/remove_typedef/input.go index d9c9b8530..cf632bae7 100644 --- a/tools/go_generics/generics_tests/remove_typedef/input.go +++ b/tools/go_generics/generics_tests/remove_typedef/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/remove_typedef/output/output.go b/tools/go_generics/generics_tests/remove_typedef/output/output.go index f111a9426..d44fd8e1c 100644 --- a/tools/go_generics/generics_tests/remove_typedef/output/output.go +++ b/tools/go_generics/generics_tests/remove_typedef/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/simple/input.go b/tools/go_generics/generics_tests/simple/input.go index 711687cf5..2a917f16c 100644 --- a/tools/go_generics/generics_tests/simple/input.go +++ b/tools/go_generics/generics_tests/simple/input.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/generics_tests/simple/output/output.go b/tools/go_generics/generics_tests/simple/output/output.go index 139c9bf9d..6bfa0b25b 100644 --- a/tools/go_generics/generics_tests/simple/output/output.go +++ b/tools/go_generics/generics_tests/simple/output/output.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/globals/globals_visitor.go b/tools/go_generics/globals/globals_visitor.go index daaa17b1d..7ae48c662 100644 --- a/tools/go_generics/globals/globals_visitor.go +++ b/tools/go_generics/globals/globals_visitor.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/globals/scope.go b/tools/go_generics/globals/scope.go index b75a91689..96c965ea2 100644 --- a/tools/go_generics/globals/scope.go +++ b/tools/go_generics/globals/scope.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/go_generics_unittest.sh b/tools/go_generics/go_generics_unittest.sh index e7553a071..44b22db91 100755 --- a/tools/go_generics/go_generics_unittest.sh +++ b/tools/go_generics/go_generics_unittest.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/tools/go_generics/go_merge/main.go b/tools/go_generics/go_merge/main.go index 2f83facf8..f6a331123 100644 --- a/tools/go_generics/go_merge/main.go +++ b/tools/go_generics/go_merge/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/imports.go b/tools/go_generics/imports.go index 57f7c3dce..3a7230c97 100644 --- a/tools/go_generics/imports.go +++ b/tools/go_generics/imports.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/remove.go b/tools/go_generics/remove.go index 139d03955..568a6bbd3 100644 --- a/tools/go_generics/remove.go +++ b/tools/go_generics/remove.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/rules_tests/template.go b/tools/go_generics/rules_tests/template.go index f3f31ae8e..aace61da1 100644 --- a/tools/go_generics/rules_tests/template.go +++ b/tools/go_generics/rules_tests/template.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_generics/rules_tests/template_test.go b/tools/go_generics/rules_tests/template_test.go index 3a38c8629..b2a3446ef 100644 --- a/tools/go_generics/rules_tests/template_test.go +++ b/tools/go_generics/rules_tests/template_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/go_stateify/main.go b/tools/go_stateify/main.go index 9e2c8e106..db7a7107b 100644 --- a/tools/go_stateify/main.go +++ b/tools/go_stateify/main.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/tools/tag_release.sh b/tools/tag_release.sh index 6906a952f..02a49cdf1 100755 --- a/tools/tag_release.sh +++ b/tools/tag_release.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2019 Google LLC +# 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. diff --git a/tools/workspace_status.sh b/tools/workspace_status.sh index a0e646e45..64a905fc9 100755 --- a/tools/workspace_status.sh +++ b/tools/workspace_status.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 Google LLC +# 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. diff --git a/vdso/barrier.h b/vdso/barrier.h index 5b6c763f6..edba4afb5 100644 --- a/vdso/barrier.h +++ b/vdso/barrier.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/vdso/check_vdso.py b/vdso/check_vdso.py index 6f7d7e7ec..e41b09709 100644 --- a/vdso/check_vdso.py +++ b/vdso/check_vdso.py @@ -1,4 +1,4 @@ -# Copyright 2018 Google LLC +# 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. diff --git a/vdso/compiler.h b/vdso/compiler.h index d65f148fb..54a510000 100644 --- a/vdso/compiler.h +++ b/vdso/compiler.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/vdso/cycle_clock.h b/vdso/cycle_clock.h index 309e07a3f..5d3fbb257 100644 --- a/vdso/cycle_clock.h +++ b/vdso/cycle_clock.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/vdso/seqlock.h b/vdso/seqlock.h index ab2f3fda3..7a173174b 100644 --- a/vdso/seqlock.h +++ b/vdso/seqlock.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/vdso/syscalls.h b/vdso/syscalls.h index 90fb424ce..f5865bb72 100644 --- a/vdso/syscalls.h +++ b/vdso/syscalls.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/vdso/vdso.cc b/vdso/vdso.cc index 550729035..6265ad217 100644 --- a/vdso/vdso.cc +++ b/vdso/vdso.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/vdso/vdso_time.cc b/vdso/vdso_time.cc index 9fc262f60..1bb4bb86b 100644 --- a/vdso/vdso_time.cc +++ b/vdso/vdso_time.cc @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. diff --git a/vdso/vdso_time.h b/vdso/vdso_time.h index 464dadff2..70d079efc 100644 --- a/vdso/vdso_time.h +++ b/vdso/vdso_time.h @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// 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. -- cgit v1.2.3 From fc9f7e3590492e3236e886a533974a786be47124 Mon Sep 17 00:00:00 2001 From: Liu Hua Date: Thu, 16 May 2019 16:19:34 -0700 Subject: tiny fix: avoid panicing when OpenSpec failed Signed-off-by: Liu Hua Change-Id: I11a4620394a10a7d92036b0341e0c21ad50bd122 PiperOrigin-RevId: 248621810 --- runsc/specutils/specutils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runsc/specutils') diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index c72207fb4..2888f55db 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -149,7 +149,7 @@ func OpenSpec(bundleDir string) (*os.File, error) { func ReadSpec(bundleDir string) (*specs.Spec, error) { specFile, err := OpenSpec(bundleDir) if err != nil { - return nil, fmt.Errorf("error opening spec file %q: %v", specFile.Name(), err) + return nil, fmt.Errorf("error opening spec file %q: %v", filepath.Join(bundleDir, "config.json"), err) } defer specFile.Close() return ReadSpecFromFile(bundleDir, specFile) -- cgit v1.2.3