diff options
Diffstat (limited to 'runsc/boot')
-rw-r--r-- | runsc/boot/BUILD | 111 | ||||
-rw-r--r-- | runsc/boot/compat_test.go | 85 | ||||
-rw-r--r-- | runsc/boot/filter/BUILD | 28 | ||||
-rw-r--r-- | runsc/boot/loader_test.go | 628 |
4 files changed, 0 insertions, 852 deletions
diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD deleted file mode 100644 index ac28c4339..000000000 --- a/runsc/boot/BUILD +++ /dev/null @@ -1,111 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -package(licenses = ["notice"]) - -go_library( - name = "boot", - srcs = [ - "compat.go", - "compat_amd64.go", - "config.go", - "controller.go", - "debug.go", - "events.go", - "fds.go", - "fs.go", - "limits.go", - "loader.go", - "network.go", - "strace.go", - ], - importpath = "gvisor.googlesource.com/gvisor/runsc/boot", - visibility = [ - "//runsc:__subpackages__", - "//test:__subpackages__", - ], - deps = [ - "//pkg/abi", - "//pkg/abi/linux", - "//pkg/control/server", - "//pkg/cpuid", - "//pkg/eventchannel", - "//pkg/log", - "//pkg/memutil", - "//pkg/rand", - "//pkg/sentry/arch", - "//pkg/sentry/arch:registers_go_proto", - "//pkg/sentry/context", - "//pkg/sentry/control", - "//pkg/sentry/fs", - "//pkg/sentry/fs/dev", - "//pkg/sentry/fs/gofer", - "//pkg/sentry/fs/host", - "//pkg/sentry/fs/proc", - "//pkg/sentry/fs/ramfs", - "//pkg/sentry/fs/sys", - "//pkg/sentry/fs/tmpfs", - "//pkg/sentry/fs/tty", - "//pkg/sentry/inet", - "//pkg/sentry/kernel", - "//pkg/sentry/kernel:uncaught_signal_go_proto", - "//pkg/sentry/kernel/auth", - "//pkg/sentry/kernel/kdefs", - "//pkg/sentry/limits", - "//pkg/sentry/loader", - "//pkg/sentry/pgalloc", - "//pkg/sentry/platform", - "//pkg/sentry/platform/kvm", - "//pkg/sentry/platform/ptrace", - "//pkg/sentry/sighandling", - "//pkg/sentry/socket/epsocket", - "//pkg/sentry/socket/hostinet", - "//pkg/sentry/socket/netlink", - "//pkg/sentry/socket/netlink/route", - "//pkg/sentry/socket/unix", - "//pkg/sentry/state", - "//pkg/sentry/strace", - "//pkg/sentry/syscalls/linux", - "//pkg/sentry/time", - "//pkg/sentry/unimpl:unimplemented_syscall_go_proto", - "//pkg/sentry/usage", - "//pkg/sentry/watchdog", - "//pkg/syserror", - "//pkg/tcpip", - "//pkg/tcpip/link/fdbased", - "//pkg/tcpip/link/loopback", - "//pkg/tcpip/link/sniffer", - "//pkg/tcpip/network/arp", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/transport/icmp", - "//pkg/tcpip/transport/tcp", - "//pkg/tcpip/transport/udp", - "//pkg/urpc", - "//runsc/boot/filter", - "//runsc/specutils", - "@com_github_golang_protobuf//proto:go_default_library", - "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", - ], -) - -go_test( - name = "boot_test", - size = "small", - srcs = [ - "compat_test.go", - "loader_test.go", - ], - embed = [":boot"], - deps = [ - "//pkg/control/server", - "//pkg/log", - "//pkg/p9", - "//pkg/sentry/arch:registers_go_proto", - "//pkg/sentry/context/contexttest", - "//pkg/sentry/fs", - "//pkg/unet", - "//runsc/fsgofer", - "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", - ], -) diff --git a/runsc/boot/compat_test.go b/runsc/boot/compat_test.go deleted file mode 100644 index ccec3d20c..000000000 --- a/runsc/boot/compat_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package boot - -import ( - "testing" - - rpb "gvisor.googlesource.com/gvisor/pkg/sentry/arch/registers_go_proto" -) - -func TestOnceTracker(t *testing.T) { - o := onceTracker{} - if !o.shouldReport(nil) { - t.Error("first call to checkAndMark, got: false, want: true") - } - o.onReported(nil) - for i := 0; i < 2; i++ { - if o.shouldReport(nil) { - t.Error("after first call to checkAndMark, got: true, want: false") - } - } -} - -func TestArgsTracker(t *testing.T) { - for _, tc := range []struct { - name string - idx []int - rdi1 uint64 - rdi2 uint64 - rsi1 uint64 - rsi2 uint64 - want bool - }{ - {name: "same rdi", idx: []int{0}, rdi1: 123, rdi2: 123, want: false}, - {name: "same rsi", idx: []int{1}, rsi1: 123, rsi2: 123, want: false}, - {name: "diff rdi", idx: []int{0}, rdi1: 123, rdi2: 321, want: true}, - {name: "diff rsi", idx: []int{1}, rsi1: 123, rsi2: 321, want: true}, - {name: "cmd is uint32", idx: []int{0}, rsi1: 0xdead00000123, rsi2: 0xbeef00000123, want: false}, - {name: "same 2 args", idx: []int{0, 1}, rsi1: 123, rdi1: 321, rsi2: 123, rdi2: 321, want: false}, - {name: "diff 2 args", idx: []int{0, 1}, rsi1: 123, rdi1: 321, rsi2: 789, rdi2: 987, want: true}, - } { - t.Run(tc.name, func(t *testing.T) { - c := newArgsTracker(tc.idx...) - regs := &rpb.AMD64Registers{Rdi: tc.rdi1, Rsi: tc.rsi1} - if !c.shouldReport(regs) { - t.Error("first call to shouldReport, got: false, want: true") - } - c.onReported(regs) - - regs.Rdi, regs.Rsi = tc.rdi2, tc.rsi2 - if got := c.shouldReport(regs); tc.want != got { - t.Errorf("second call to shouldReport, got: %t, want: %t", got, tc.want) - } - }) - } -} - -func TestArgsTrackerLimit(t *testing.T) { - c := newArgsTracker(0, 1) - for i := 0; i < reportLimit; i++ { - regs := &rpb.AMD64Registers{Rdi: 123, Rsi: uint64(i)} - if !c.shouldReport(regs) { - t.Error("shouldReport before limit was reached, got: false, want: true") - } - c.onReported(regs) - } - - // Should hit the count limit now. - regs := &rpb.AMD64Registers{Rdi: 123, Rsi: 123456} - if c.shouldReport(regs) { - t.Error("shouldReport after limit was reached, got: true, want: false") - } -} diff --git a/runsc/boot/filter/BUILD b/runsc/boot/filter/BUILD deleted file mode 100644 index 3b6020cf3..000000000 --- a/runsc/boot/filter/BUILD +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -package(licenses = ["notice"]) - -go_library( - name = "filter", - srcs = [ - "config.go", - "extra_filters.go", - "extra_filters_msan.go", - "extra_filters_race.go", - "filter.go", - ], - importpath = "gvisor.googlesource.com/gvisor/runsc/boot/filter", - visibility = [ - "//runsc/boot:__subpackages__", - ], - deps = [ - "//pkg/abi/linux", - "//pkg/log", - "//pkg/seccomp", - "//pkg/sentry/platform", - "//pkg/sentry/platform/kvm", - "//pkg/sentry/platform/ptrace", - "//pkg/tcpip/link/fdbased", - "@org_golang_x_sys//unix:go_default_library", - ], -) diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go deleted file mode 100644 index 6393cb3fb..000000000 --- a/runsc/boot/loader_test.go +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package boot - -import ( - "fmt" - "math/rand" - "os" - "reflect" - "sync" - "syscall" - "testing" - "time" - - 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/p9" - "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" - "gvisor.googlesource.com/gvisor/pkg/sentry/fs" - "gvisor.googlesource.com/gvisor/pkg/unet" - "gvisor.googlesource.com/gvisor/runsc/fsgofer" -) - -func init() { - log.SetLevel(log.Debug) - rand.Seed(time.Now().UnixNano()) -} - -func testConfig() *Config { - return &Config{ - RootDir: "unused_root_dir", - Network: NetworkNone, - DisableSeccomp: true, - } -} - -// testSpec returns a simple spec that can be used in tests. -func testSpec() *specs.Spec { - return &specs.Spec{ - // The host filesystem root is the sandbox root. - Root: &specs.Root{ - Path: "/", - Readonly: true, - }, - Process: &specs.Process{ - Args: []string{"/bin/true"}, - }, - } -} - -// startGofer starts a new gofer routine serving 'root' path. It returns the -// sandbox side of the connection, and a function that when called will stop the -// gofer. -func startGofer(root string) (int, func(), error) { - fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) - if err != nil { - return 0, nil, err - } - sandboxEnd, goferEnd := fds[0], fds[1] - - socket, err := unet.NewSocket(goferEnd) - if err != nil { - syscall.Close(sandboxEnd) - syscall.Close(goferEnd) - return 0, nil, fmt.Errorf("error creating server on FD %d: %v", goferEnd, err) - } - at, err := fsgofer.NewAttachPoint(root, fsgofer.Config{ROMount: true}) - if err != nil { - return 0, nil, err - } - go func() { - s := p9.NewServer(at) - if err := s.Handle(socket); err != nil { - log.Infof("Gofer is stopping. FD: %d, err: %v\n", goferEnd, err) - } - }() - // Closing the gofer socket will stop the gofer and exit goroutine above. - cleanup := func() { - if err := socket.Close(); err != nil { - log.Warningf("Error closing gofer socket: %v", err) - } - } - return sandboxEnd, cleanup, nil -} - -func createLoader() (*Loader, func(), error) { - fd, err := server.CreateSocket(ControlSocketAddr(fmt.Sprintf("%010d", rand.Int())[:10])) - if err != nil { - return nil, nil, err - } - conf := testConfig() - spec := testSpec() - - sandEnd, cleanup, err := startGofer(spec.Root.Path) - if err != nil { - return nil, nil, err - } - - stdio := []int{int(os.Stdin.Fd()), int(os.Stdout.Fd()), int(os.Stderr.Fd())} - args := Args{ - ID: "foo", - Spec: spec, - Conf: conf, - ControllerFD: fd, - GoferFDs: []int{sandEnd}, - StdioFDs: stdio, - } - l, err := New(args) - if err != nil { - cleanup() - return nil, nil, err - } - return l, cleanup, nil -} - -// TestRun runs a simple application in a sandbox and checks that it succeeds. -func TestRun(t *testing.T) { - l, cleanup, err := createLoader() - if err != nil { - t.Fatalf("error creating loader: %v", err) - } - defer l.Destroy() - defer cleanup() - - // Start a goroutine to read the start chan result, otherwise Run will - // block forever. - var resultChanErr error - var wg sync.WaitGroup - wg.Add(1) - go func() { - resultChanErr = <-l.ctrl.manager.startResultChan - wg.Done() - }() - - // Run the container. - if err := l.Run(); err != nil { - t.Errorf("error running container: %v", err) - } - - // We should have not gotten an error on the startResultChan. - wg.Wait() - if resultChanErr != nil { - t.Errorf("error on startResultChan: %v", resultChanErr) - } - - // Wait for the application to exit. It should succeed. - if status := l.WaitExit(); status.Code != 0 || status.Signo != 0 { - t.Errorf("application exited with status %+v, want 0", status) - } -} - -// TestStartSignal tests that the controller Start message will cause -// WaitForStartSignal to return. -func TestStartSignal(t *testing.T) { - l, cleanup, err := createLoader() - if err != nil { - t.Fatalf("error creating loader: %v", err) - } - defer l.Destroy() - defer cleanup() - - // We aren't going to wait on this application, so the control server - // needs to be shut down manually. - defer l.ctrl.srv.Stop() - - // Start a goroutine that calls WaitForStartSignal and writes to a - // channel when it returns. - waitFinished := make(chan struct{}) - go func() { - l.WaitForStartSignal() - // Pretend that Run() executed and returned no error. - l.ctrl.manager.startResultChan <- nil - waitFinished <- struct{}{} - }() - - // Nothing has been written to the channel, so waitFinished should not - // return. Give it a little bit of time to make sure the goroutine has - // started. - select { - case <-waitFinished: - t.Errorf("WaitForStartSignal completed but it should not have") - case <-time.After(50 * time.Millisecond): - // OK. - } - - // Trigger the control server StartRoot method. - cid := "foo" - if err := l.ctrl.manager.StartRoot(&cid, nil); err != nil { - t.Errorf("error calling StartRoot: %v", err) - } - - // Now WaitForStartSignal should return (within a short amount of - // time). - select { - case <-waitFinished: - // OK. - case <-time.After(50 * time.Millisecond): - t.Errorf("WaitForStartSignal did not complete but it should have") - } - -} - -// Test that MountNamespace can be created with various specs. -func TestCreateMountNamespace(t *testing.T) { - testCases := []struct { - name string - // Spec that will be used to create the mount manager. Note - // that we can't mount procfs without a kernel, so each spec - // MUST contain something other than procfs mounted at /proc. - spec specs.Spec - // Paths that are expected to exist in the resulting fs. - expectedPaths []string - }{ - { - // Only proc. - name: "only proc mount", - spec: specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/proc", - Type: "tmpfs", - }, - }, - }, - // /proc, /dev, and /sys should always be mounted. - expectedPaths: []string{"/proc", "/dev", "/sys"}, - }, - { - // Mount at a deep path, with many components that do - // not exist in the root. - name: "deep mount path", - spec: specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/some/very/very/deep/path", - Type: "tmpfs", - }, - { - Destination: "/proc", - Type: "tmpfs", - }, - }, - }, - // /some/deep/path should be mounted, along with /proc, - // /dev, and /sys. - expectedPaths: []string{"/some/very/very/deep/path", "/proc", "/dev", "/sys"}, - }, - { - // Mounts are nested inside each other. - name: "nested mounts", - spec: specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/proc", - Type: "tmpfs", - }, - { - Destination: "/foo", - Type: "tmpfs", - }, - { - Destination: "/foo/qux", - Type: "tmpfs", - }, - { - // File mounts with the same prefix. - Destination: "/foo/qux-quz", - Type: "tmpfs", - }, - { - Destination: "/foo/bar", - Type: "tmpfs", - }, - { - Destination: "/foo/bar/baz", - Type: "tmpfs", - }, - { - // A deep path that is in foo but not the other mounts. - Destination: "/foo/some/very/very/deep/path", - Type: "tmpfs", - }, - }, - }, - 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", - Type: "tmpfs", - }, - { - // Unsupported fs type. - Destination: "/dev/mqueue", - Type: "mqueue", - }, - { - Destination: "/dev/foo", - Type: "tmpfs", - }, - { - Destination: "/dev/bar", - Type: "tmpfs", - }, - }, - }, - expectedPaths: []string{"/proc", "/dev", "/dev/fd-foo", "/dev/foo", "/dev/bar", "/sys"}, - }, - { - name: "mounts inside mandatory mounts", - spec: specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/proc", - Type: "tmpfs", - }, - // We don't include /sys, and /tmp in - // the spec, since they will be added - // automatically. - // - // Instead, add submounts inside these - // directories and make sure they are - // visible under the mandatory mounts. - { - Destination: "/sys/bar", - Type: "tmpfs", - }, - { - Destination: "/tmp/baz", - Type: "tmpfs", - }, - }, - }, - expectedPaths: []string{"/proc", "/sys", "/sys/bar", "/tmp", "/tmp/baz"}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - conf := testConfig() - ctx := contexttest.Context(t) - - sandEnd, cleanup, err := startGofer(tc.spec.Root.Path) - if err != nil { - t.Fatalf("failed to create gofer: %v", err) - } - defer cleanup() - - // setupRootContainer needs to find root from the context after the - // namespace is created. - var mns *fs.MountNamespace - setMountNS := func(m *fs.MountNamespace) { - mns = m - ctx.(*contexttest.TestContext).RegisterValue(fs.CtxRoot, mns.Root()) - } - mntr := newContainerMounter(&tc.spec, "", []int{sandEnd}, nil) - if err := mntr.setupRootContainer(ctx, ctx, conf, setMountNS); err != nil { - t.Fatalf("createMountNamespace test case %q failed: %v", tc.name, err) - } - root := mns.Root() - defer root.DecRef() - for _, p := range tc.expectedPaths { - maxTraversals := uint(0) - if d, err := mns.FindInode(ctx, root, root, p, &maxTraversals); err != nil { - t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err) - } else { - d.DecRef() - } - } - }) - } -} - -// TestRestoreEnvironment tests that the correct mounts are collected from the spec and config -// in order to build the environment for restoring. -func TestRestoreEnvironment(t *testing.T) { - testCases := []struct { - name string - spec *specs.Spec - ioFDs []int - errorExpected bool - expectedRenv fs.RestoreEnvironment - }{ - { - name: "basic spec test", - spec: &specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/some/very/very/deep/path", - Type: "tmpfs", - }, - { - Destination: "/proc", - Type: "tmpfs", - }, - }, - }, - ioFDs: []int{0}, - errorExpected: false, - expectedRenv: fs.RestoreEnvironment{ - MountSources: map[string][]fs.MountArgs{ - "9p": { - { - Dev: "9pfs-/", - Flags: fs.MountSourceFlags{ReadOnly: true}, - DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true,cache=remote_revalidating", - }, - }, - "tmpfs": { - { - Dev: "none", - }, - { - Dev: "none", - }, - { - Dev: "none", - }, - }, - "devtmpfs": { - { - Dev: "none", - }, - }, - "devpts": { - { - Dev: "none", - }, - }, - "sysfs": { - { - Dev: "none", - }, - }, - }, - }, - }, - { - name: "bind type test", - spec: &specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/dev/fd-foo", - Type: "bind", - }, - }, - }, - ioFDs: []int{0, 1}, - errorExpected: false, - expectedRenv: fs.RestoreEnvironment{ - MountSources: map[string][]fs.MountArgs{ - "9p": { - { - Dev: "9pfs-/", - Flags: fs.MountSourceFlags{ReadOnly: true}, - DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true,cache=remote_revalidating", - }, - { - Dev: "9pfs-/dev/fd-foo", - DataString: "trans=fd,rfdno=1,wfdno=1,privateunixsocket=true,cache=remote_revalidating", - }, - }, - "tmpfs": { - { - Dev: "none", - }, - }, - "devtmpfs": { - { - Dev: "none", - }, - }, - "devpts": { - { - Dev: "none", - }, - }, - "proc": { - { - Dev: "none", - }, - }, - "sysfs": { - { - Dev: "none", - }, - }, - }, - }, - }, - { - name: "options test", - spec: &specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/dev/fd-foo", - Type: "tmpfs", - Options: []string{"uid=1022", "noatime"}, - }, - }, - }, - ioFDs: []int{0}, - errorExpected: false, - expectedRenv: fs.RestoreEnvironment{ - MountSources: map[string][]fs.MountArgs{ - "9p": { - { - Dev: "9pfs-/", - Flags: fs.MountSourceFlags{ReadOnly: true}, - DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true,cache=remote_revalidating", - }, - }, - "tmpfs": { - { - Dev: "none", - Flags: fs.MountSourceFlags{NoAtime: true}, - DataString: "uid=1022", - }, - { - Dev: "none", - }, - }, - "devtmpfs": { - { - Dev: "none", - }, - }, - "devpts": { - { - Dev: "none", - }, - }, - "proc": { - { - Dev: "none", - }, - }, - "sysfs": { - { - Dev: "none", - }, - }, - }, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - conf := testConfig() - mntr := newContainerMounter(tc.spec, "", tc.ioFDs, nil) - actualRenv, err := mntr.createRestoreEnvironment(conf) - if !tc.errorExpected && err != nil { - t.Fatalf("could not create restore environment for test:%s", tc.name) - } else if tc.errorExpected { - if err == nil { - t.Errorf("expected an error, but no error occurred.") - } - } else { - if !reflect.DeepEqual(*actualRenv, tc.expectedRenv) { - t.Errorf("restore environments did not match for test:%s\ngot:%+v\nwant:%+v\n", tc.name, *actualRenv, tc.expectedRenv) - } - } - }) - } -} |