summaryrefslogtreecommitdiffhomepage
path: root/runsc/boot
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/boot')
-rw-r--r--runsc/boot/BUILD150
-rw-r--r--runsc/boot/boot_amd64_state_autogen.go3
-rw-r--r--runsc/boot/boot_arm64_state_autogen.go3
-rw-r--r--runsc/boot/boot_state_autogen.go39
-rw-r--r--runsc/boot/compat_test.go90
-rw-r--r--runsc/boot/filter/BUILD28
-rw-r--r--runsc/boot/filter/filter_amd64_state_autogen.go5
-rw-r--r--runsc/boot/filter/filter_arm64_state_autogen.go5
-rw-r--r--runsc/boot/filter/filter_race_state_autogen.go5
-rw-r--r--runsc/boot/filter/filter_state_autogen.go6
-rw-r--r--runsc/boot/fs_test.go252
-rw-r--r--runsc/boot/loader_test.go731
-rw-r--r--runsc/boot/platforms/BUILD15
-rw-r--r--runsc/boot/platforms/platforms_state_autogen.go3
-rw-r--r--runsc/boot/pprof/BUILD11
-rw-r--r--runsc/boot/pprof/pprof_state_autogen.go3
16 files changed, 72 insertions, 1277 deletions
diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD
deleted file mode 100644
index c9d2b3eff..000000000
--- a/runsc/boot/BUILD
+++ /dev/null
@@ -1,150 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "boot",
- srcs = [
- "compat.go",
- "compat_amd64.go",
- "compat_arm64.go",
- "controller.go",
- "debug.go",
- "events.go",
- "fs.go",
- "limits.go",
- "loader.go",
- "network.go",
- "strace.go",
- "vfs.go",
- ],
- visibility = [
- "//pkg/test:__subpackages__",
- "//runsc:__subpackages__",
- "//test:__subpackages__",
- ],
- deps = [
- "//pkg/abi",
- "//pkg/abi/linux",
- "//pkg/bpf",
- "//pkg/cleanup",
- "//pkg/context",
- "//pkg/control/server",
- "//pkg/coverage",
- "//pkg/cpuid",
- "//pkg/errors/linuxerr",
- "//pkg/eventchannel",
- "//pkg/fd",
- "//pkg/flipcall",
- "//pkg/fspath",
- "//pkg/log",
- "//pkg/memutil",
- "//pkg/rand",
- "//pkg/refs",
- "//pkg/refsvfs2",
- "//pkg/sentry/arch",
- "//pkg/sentry/arch:registers_go_proto",
- "//pkg/sentry/control",
- "//pkg/sentry/devices/memdev",
- "//pkg/sentry/devices/ttydev",
- "//pkg/sentry/devices/tundev",
- "//pkg/sentry/fdimport",
- "//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/fs/user",
- "//pkg/sentry/fsimpl/cgroupfs",
- "//pkg/sentry/fsimpl/devpts",
- "//pkg/sentry/fsimpl/devtmpfs",
- "//pkg/sentry/fsimpl/fuse",
- "//pkg/sentry/fsimpl/gofer",
- "//pkg/sentry/fsimpl/host",
- "//pkg/sentry/fsimpl/overlay",
- "//pkg/sentry/fsimpl/proc",
- "//pkg/sentry/fsimpl/sys",
- "//pkg/sentry/fsimpl/tmpfs",
- "//pkg/sentry/fsimpl/verity",
- "//pkg/sentry/inet",
- "//pkg/sentry/kernel",
- "//pkg/sentry/kernel:uncaught_signal_go_proto",
- "//pkg/sentry/kernel/auth",
- "//pkg/sentry/limits",
- "//pkg/sentry/loader",
- "//pkg/sentry/pgalloc",
- "//pkg/sentry/platform",
- "//pkg/sentry/sighandling",
- "//pkg/sentry/socket/hostinet",
- "//pkg/sentry/socket/netfilter",
- "//pkg/sentry/socket/netlink",
- "//pkg/sentry/socket/netlink/route",
- "//pkg/sentry/socket/netlink/uevent",
- "//pkg/sentry/socket/netstack",
- "//pkg/sentry/socket/unix",
- "//pkg/sentry/state",
- "//pkg/sentry/strace",
- "//pkg/sentry/syscalls/linux/vfs2",
- "//pkg/sentry/time",
- "//pkg/sentry/unimpl:unimplemented_syscall_go_proto",
- "//pkg/sentry/usage",
- "//pkg/sentry/vfs",
- "//pkg/sentry/watchdog",
- "//pkg/sync",
- "//pkg/tcpip",
- "//pkg/tcpip/link/fdbased",
- "//pkg/tcpip/link/loopback",
- "//pkg/tcpip/link/packetsocket",
- "//pkg/tcpip/link/qdisc/fifo",
- "//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/raw",
- "//pkg/tcpip/transport/tcp",
- "//pkg/tcpip/transport/udp",
- "//pkg/urpc",
- "//runsc/boot/filter",
- "//runsc/boot/platforms",
- "//runsc/boot/pprof",
- "//runsc/config",
- "//runsc/specutils",
- "//runsc/specutils/seccomp",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@org_golang_google_protobuf//proto:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "boot_test",
- size = "small",
- srcs = [
- "compat_test.go",
- "fs_test.go",
- "loader_test.go",
- ],
- library = ":boot",
- deps = [
- "//pkg/control/server",
- "//pkg/fd",
- "//pkg/fspath",
- "//pkg/log",
- "//pkg/p9",
- "//pkg/sentry/contexttest",
- "//pkg/sentry/fs",
- "//pkg/sentry/vfs",
- "//pkg/sync",
- "//pkg/unet",
- "//runsc/config",
- "//runsc/fsgofer",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/boot/boot_amd64_state_autogen.go b/runsc/boot/boot_amd64_state_autogen.go
new file mode 100644
index 000000000..23dd4b7b3
--- /dev/null
+++ b/runsc/boot/boot_amd64_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package boot
diff --git a/runsc/boot/boot_arm64_state_autogen.go b/runsc/boot/boot_arm64_state_autogen.go
new file mode 100644
index 000000000..23dd4b7b3
--- /dev/null
+++ b/runsc/boot/boot_arm64_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package boot
diff --git a/runsc/boot/boot_state_autogen.go b/runsc/boot/boot_state_autogen.go
new file mode 100644
index 000000000..0f6746d1f
--- /dev/null
+++ b/runsc/boot/boot_state_autogen.go
@@ -0,0 +1,39 @@
+// automatically generated by stateify.
+
+package boot
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (f *sandboxNetstackCreator) StateTypeName() string {
+ return "runsc/boot.sandboxNetstackCreator"
+}
+
+func (f *sandboxNetstackCreator) StateFields() []string {
+ return []string{
+ "clock",
+ "uniqueID",
+ }
+}
+
+func (f *sandboxNetstackCreator) beforeSave() {}
+
+// +checklocksignore
+func (f *sandboxNetstackCreator) StateSave(stateSinkObject state.Sink) {
+ f.beforeSave()
+ stateSinkObject.Save(0, &f.clock)
+ stateSinkObject.Save(1, &f.uniqueID)
+}
+
+func (f *sandboxNetstackCreator) afterLoad() {}
+
+// +checklocksignore
+func (f *sandboxNetstackCreator) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &f.clock)
+ stateSourceObject.Load(1, &f.uniqueID)
+}
+
+func init() {
+ state.Register((*sandboxNetstackCreator)(nil))
+}
diff --git a/runsc/boot/compat_test.go b/runsc/boot/compat_test.go
deleted file mode 100644
index 839c5303b..000000000
--- a/runsc/boot/compat_test.go
+++ /dev/null
@@ -1,90 +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"
-)
-
-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
- arg1_1 uint64
- arg1_2 uint64
- arg2_1 uint64
- arg2_2 uint64
- want bool
- }{
- {name: "same arg1", idx: []int{0}, arg1_1: 123, arg1_2: 123, want: false},
- {name: "same arg2", idx: []int{1}, arg2_1: 123, arg2_2: 123, want: false},
- {name: "diff arg1", idx: []int{0}, arg1_1: 123, arg1_2: 321, want: true},
- {name: "diff arg2", idx: []int{1}, arg2_1: 123, arg2_2: 321, want: true},
- {name: "cmd is uint32", idx: []int{0}, arg2_1: 0xdead00000123, arg2_2: 0xbeef00000123, want: false},
- {name: "same 2 args", idx: []int{0, 1}, arg2_1: 123, arg1_1: 321, arg2_2: 123, arg1_2: 321, want: false},
- {name: "diff 2 args", idx: []int{0, 1}, arg2_1: 123, arg1_1: 321, arg2_2: 789, arg1_2: 987, want: true},
- } {
- t.Run(tc.name, func(t *testing.T) {
- c := newArgsTracker(tc.idx...)
- regs := newRegs()
- setArgVal(0, tc.arg1_1, regs)
- setArgVal(1, tc.arg2_1, regs)
- if !c.shouldReport(regs) {
- t.Error("first call to shouldReport, got: false, want: true")
- }
- c.onReported(regs)
-
- setArgVal(0, tc.arg1_2, regs)
- setArgVal(1, tc.arg2_2, regs)
- 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 := newRegs()
- setArgVal(0, 123, regs)
- setArgVal(1, uint64(i), regs)
- 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 := newRegs()
- setArgVal(0, 123, regs)
- setArgVal(1, 123456, regs)
- 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 ed18f0047..000000000
--- a/runsc/boot/filter/BUILD
+++ /dev/null
@@ -1,28 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "filter",
- srcs = [
- "config.go",
- "config_amd64.go",
- "config_arm64.go",
- "config_profile.go",
- "extra_filters.go",
- "extra_filters_msan.go",
- "extra_filters_race.go",
- "filter.go",
- ],
- visibility = [
- "//runsc/boot:__subpackages__",
- ],
- deps = [
- "//pkg/abi/linux",
- "//pkg/log",
- "//pkg/seccomp",
- "//pkg/sentry/platform",
- "//pkg/tcpip/link/fdbased",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/boot/filter/filter_amd64_state_autogen.go b/runsc/boot/filter/filter_amd64_state_autogen.go
new file mode 100644
index 000000000..0f27e5568
--- /dev/null
+++ b/runsc/boot/filter/filter_amd64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build amd64
+
+package filter
diff --git a/runsc/boot/filter/filter_arm64_state_autogen.go b/runsc/boot/filter/filter_arm64_state_autogen.go
new file mode 100644
index 000000000..e87cf5af7
--- /dev/null
+++ b/runsc/boot/filter/filter_arm64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build arm64
+
+package filter
diff --git a/runsc/boot/filter/filter_race_state_autogen.go b/runsc/boot/filter/filter_race_state_autogen.go
new file mode 100644
index 000000000..c4a858e80
--- /dev/null
+++ b/runsc/boot/filter/filter_race_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build race
+
+package filter
diff --git a/runsc/boot/filter/filter_state_autogen.go b/runsc/boot/filter/filter_state_autogen.go
new file mode 100644
index 000000000..41ff99424
--- /dev/null
+++ b/runsc/boot/filter/filter_state_autogen.go
@@ -0,0 +1,6 @@
+// automatically generated by stateify.
+
+// +build !msan,!race
+// +build msan
+
+package filter
diff --git a/runsc/boot/fs_test.go b/runsc/boot/fs_test.go
deleted file mode 100644
index 09ffda628..000000000
--- a/runsc/boot/fs_test.go
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright 2019 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package boot
-
-import (
- "reflect"
- "strings"
- "testing"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/runsc/config"
-)
-
-func TestPodMountHintsHappy(t *testing.T) {
- spec := &specs.Spec{
- Annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
-
- MountPrefix + "mount2.source": "bar",
- MountPrefix + "mount2.type": "bind",
- MountPrefix + "mount2.share": "container",
- MountPrefix + "mount2.options": "rw,private",
- },
- }
- podHints, err := newPodMountHints(spec)
- if err != nil {
- t.Fatalf("newPodMountHints failed: %v", err)
- }
-
- // Check that fields were set correctly.
- mount1 := podHints.mounts["mount1"]
- if want := "mount1"; want != mount1.name {
- t.Errorf("mount1 name, want: %q, got: %q", want, mount1.name)
- }
- if want := "foo"; want != mount1.mount.Source {
- t.Errorf("mount1 source, want: %q, got: %q", want, mount1.mount.Source)
- }
- if want := "tmpfs"; want != mount1.mount.Type {
- t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Type)
- }
- if want := pod; want != mount1.share {
- t.Errorf("mount1 type, want: %q, got: %q", want, mount1.share)
- }
- if want := []string(nil); !reflect.DeepEqual(want, mount1.mount.Options) {
- t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Options)
- }
-
- mount2 := podHints.mounts["mount2"]
- if want := "mount2"; want != mount2.name {
- t.Errorf("mount2 name, want: %q, got: %q", want, mount2.name)
- }
- if want := "bar"; want != mount2.mount.Source {
- t.Errorf("mount2 source, want: %q, got: %q", want, mount2.mount.Source)
- }
- if want := "bind"; want != mount2.mount.Type {
- t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Type)
- }
- if want := container; want != mount2.share {
- t.Errorf("mount2 type, want: %q, got: %q", want, mount2.share)
- }
- if want := []string{"private", "rw"}; !reflect.DeepEqual(want, mount2.mount.Options) {
- t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Options)
- }
-}
-
-func TestPodMountHintsErrors(t *testing.T) {
- for _, tst := range []struct {
- name string
- annotations map[string]string
- error string
- }{
- {
- name: "too short",
- annotations: map[string]string{
- MountPrefix + "mount1": "foo",
- },
- error: "invalid mount annotation",
- },
- {
- name: "no name",
- annotations: map[string]string{
- MountPrefix + ".source": "foo",
- },
- error: "invalid mount name",
- },
- {
- name: "missing source",
- annotations: map[string]string{
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
- },
- error: "source field",
- },
- {
- name: "missing type",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.share": "pod",
- },
- error: "type field",
- },
- {
- name: "missing share",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- },
- error: "share field",
- },
- {
- name: "invalid field name",
- annotations: map[string]string{
- MountPrefix + "mount1.invalid": "foo",
- },
- error: "invalid mount annotation",
- },
- {
- name: "invalid source",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
- },
- error: "source cannot be empty",
- },
- {
- name: "invalid type",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "invalid-type",
- MountPrefix + "mount1.share": "pod",
- },
- error: "invalid type",
- },
- {
- name: "invalid share",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "invalid-share",
- },
- error: "invalid share",
- },
- {
- name: "invalid options",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
- MountPrefix + "mount1.options": "invalid-option",
- },
- error: "unknown mount option",
- },
- {
- name: "duplicate source",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
-
- MountPrefix + "mount2.source": "foo",
- MountPrefix + "mount2.type": "bind",
- MountPrefix + "mount2.share": "container",
- },
- error: "have the same mount source",
- },
- } {
- t.Run(tst.name, func(t *testing.T) {
- spec := &specs.Spec{Annotations: tst.annotations}
- podHints, err := newPodMountHints(spec)
- if err == nil || !strings.Contains(err.Error(), tst.error) {
- t.Errorf("newPodMountHints invalid error, want: .*%s.*, got: %v", tst.error, err)
- }
- if podHints != nil {
- t.Errorf("newPodMountHints must return nil on failure: %+v", podHints)
- }
- })
- }
-}
-
-func TestGetMountAccessType(t *testing.T) {
- const source = "foo"
- for _, tst := range []struct {
- name string
- annotations map[string]string
- want config.FileAccessType
- }{
- {
- name: "container=exclusive",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source,
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "container",
- },
- want: config.FileAccessExclusive,
- },
- {
- name: "pod=shared",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source,
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "pod",
- },
- want: config.FileAccessShared,
- },
- {
- name: "shared=shared",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source,
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "shared",
- },
- want: config.FileAccessShared,
- },
- {
- name: "default=shared",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source + "mismatch",
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "container",
- },
- want: config.FileAccessShared,
- },
- } {
- t.Run(tst.name, func(t *testing.T) {
- spec := &specs.Spec{Annotations: tst.annotations}
- podHints, err := newPodMountHints(spec)
- if err != nil {
- t.Fatalf("newPodMountHints failed: %v", err)
- }
- mounter := containerMounter{hints: podHints}
- conf := &config.Config{FileAccessMounts: config.FileAccessShared}
- if got := mounter.getMountAccessType(conf, &specs.Mount{Source: source}); got != tst.want {
- t.Errorf("getMountAccessType(), want: %v, got: %v", tst.want, got)
- }
- })
- }
-}
diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go
deleted file mode 100644
index b5e8d08a5..000000000
--- a/runsc/boot/loader_test.go
+++ /dev/null
@@ -1,731 +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"
- "testing"
- "time"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/control/server"
- "gvisor.dev/gvisor/pkg/fd"
- "gvisor.dev/gvisor/pkg/fspath"
- "gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/pkg/p9"
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/fs"
- "gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/unet"
- "gvisor.dev/gvisor/runsc/config"
- "gvisor.dev/gvisor/runsc/fsgofer"
-)
-
-func init() {
- log.SetLevel(log.Debug)
- rand.Seed(time.Now().UnixNano())
- if err := fsgofer.OpenProcSelfFD(); err != nil {
- panic(err)
- }
- config.RegisterFlags()
-}
-
-func testConfig() *config.Config {
- conf, err := config.NewFromFlags()
- if err != nil {
- panic(err)
- }
- // Change test defaults.
- conf.RootDir = "unused_root_dir"
- conf.Network = config.NetworkNone
- conf.DisableSeccomp = true
- return conf
-}
-
-// 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 := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
- if err != nil {
- return 0, nil, err
- }
- sandboxEnd, goferEnd := fds[0], fds[1]
-
- socket, err := unet.NewSocket(goferEnd)
- if err != nil {
- unix.Close(sandboxEnd)
- unix.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(vfsEnabled bool, spec *specs.Spec) (*Loader, func(), error) {
- fd, err := server.CreateSocket(ControlSocketAddr(fmt.Sprintf("%010d", rand.Int())[:10]))
- if err != nil {
- return nil, nil, err
- }
- conf := testConfig()
- conf.VFS2 = vfsEnabled
-
- sandEnd, cleanup, err := startGofer(spec.Root.Path)
- if err != nil {
- return nil, nil, err
- }
-
- // Loader takes ownership of stdio.
- var stdio []int
- for _, f := range []*os.File{os.Stdin, os.Stdout, os.Stderr} {
- newFd, err := unix.Dup(int(f.Fd()))
- if err != nil {
- return nil, nil, err
- }
- stdio = append(stdio, newFd)
- }
-
- 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) {
- doRun(t, false)
-}
-
-// TestRunVFS2 runs TestRun in VFSv2.
-func TestRunVFS2(t *testing.T) {
- doRun(t, true)
-}
-
-func doRun(t *testing.T, vfsEnabled bool) {
- l, cleanup, err := createLoader(vfsEnabled, testSpec())
- 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) {
- doStartSignal(t, false)
-}
-
-// TestStartSignalVFS2 does TestStartSignal with VFS2.
-func TestStartSignalVFS2(t *testing.T) {
- doStartSignal(t, true)
-}
-
-func doStartSignal(t *testing.T, vfsEnabled bool) {
- l, cleanup, err := createLoader(vfsEnabled, testSpec())
- 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(time.Hour)
-
- // 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")
- }
-
-}
-
-type CreateMountTestcase 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
-}
-
-func createMountTestcases() []*CreateMountTestcase {
- testCases := []*CreateMountTestcase{
- {
- // 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",
- },
- {
- Destination: "/sys/bar",
- Type: "tmpfs",
- },
- {
- Destination: "/tmp/baz",
- Type: "tmpfs",
- },
- {
- Destination: "/dev/goo",
- Type: "tmpfs",
- },
- },
- },
- expectedPaths: []string{"/proc", "/sys", "/sys/bar", "/tmp", "/tmp/baz", "/dev/goo"},
- },
- }
-
- return testCases
-}
-
-// Test that MountNamespace can be created with various specs.
-func TestCreateMountNamespace(t *testing.T) {
- for _, tc := range createMountTestcases() {
- 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()
-
- info := containerInfo{
- conf: conf,
- spec: &tc.spec,
- goferFDs: []*fd.FD{fd.New(sandEnd)},
- }
-
- mntr := newContainerMounter(&info, nil, &podMountHints{}, false /* vfs2Enabled */)
- mns, err := mntr.createMountNamespace(ctx, conf)
- if err != nil {
- t.Fatalf("failed to create mount namespace: %v", err)
- }
- ctx = fs.WithRoot(ctx, mns.Root())
- if err := mntr.mountSubmounts(ctx, conf, mns); err != nil {
- t.Fatalf("failed to create mount namespace: %v", err)
- }
-
- root := mns.Root()
- defer root.DecRef(ctx)
- 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(ctx)
- }
- }
- })
- }
-}
-
-// Test that MountNamespace can be created with various specs.
-func TestCreateMountNamespaceVFS2(t *testing.T) {
- for _, tc := range createMountTestcases() {
- t.Run(tc.name, func(t *testing.T) {
- spec := testSpec()
- spec.Mounts = tc.spec.Mounts
- spec.Root = tc.spec.Root
-
- t.Logf("Using root: %q", spec.Root.Path)
- l, loaderCleanup, err := createLoader(true /* VFS2 Enabled */, spec)
- if err != nil {
- t.Fatalf("failed to create loader: %v", err)
- }
- defer l.Destroy()
- defer loaderCleanup()
-
- mntr := newContainerMounter(&l.root, l.k, l.mountHints, true /* vfs2Enabled */)
- if err := mntr.processHints(l.root.conf, l.root.procArgs.Credentials); err != nil {
- t.Fatalf("failed process hints: %v", err)
- }
-
- ctx := l.k.SupervisorContext()
- mns, err := mntr.mountAll(l.root.conf, &l.root.procArgs)
- if err != nil {
- t.Fatalf("mountAll: %v", err)
- }
-
- root := mns.Root()
- root.IncRef()
- defer root.DecRef(ctx)
- for _, p := range tc.expectedPaths {
- target := &vfs.PathOperation{
- Root: root,
- Start: root,
- Path: fspath.Parse(p),
- }
-
- if d, err := l.k.VFS().GetDentryAt(ctx, l.root.procArgs.Credentials, target, &vfs.GetDentryOptions{}); err != nil {
- t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err)
- } else {
- d.DecRef(ctx)
- }
- }
- })
- }
-}
-
-// 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",
- },
- },
- "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",
- },
- {
- 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",
- },
- },
- "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()
- var ioFDs []*fd.FD
- for _, ioFD := range tc.ioFDs {
- ioFDs = append(ioFDs, fd.New(ioFD))
- }
- info := containerInfo{
- conf: conf,
- spec: tc.spec,
- goferFDs: ioFDs,
- }
- mntr := newContainerMounter(&info, nil, &podMountHints{}, false /* vfs2Enabled */)
- 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)
- }
- }
- })
- }
-}
diff --git a/runsc/boot/platforms/BUILD b/runsc/boot/platforms/BUILD
deleted file mode 100644
index 77774f43c..000000000
--- a/runsc/boot/platforms/BUILD
+++ /dev/null
@@ -1,15 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "platforms",
- srcs = ["platforms.go"],
- visibility = [
- "//runsc:__subpackages__",
- ],
- deps = [
- "//pkg/sentry/platform/kvm",
- "//pkg/sentry/platform/ptrace",
- ],
-)
diff --git a/runsc/boot/platforms/platforms_state_autogen.go b/runsc/boot/platforms/platforms_state_autogen.go
new file mode 100644
index 000000000..8676d25c1
--- /dev/null
+++ b/runsc/boot/platforms/platforms_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package platforms
diff --git a/runsc/boot/pprof/BUILD b/runsc/boot/pprof/BUILD
deleted file mode 100644
index 29cb42b2f..000000000
--- a/runsc/boot/pprof/BUILD
+++ /dev/null
@@ -1,11 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "pprof",
- srcs = ["pprof.go"],
- visibility = [
- "//runsc:__subpackages__",
- ],
-)
diff --git a/runsc/boot/pprof/pprof_state_autogen.go b/runsc/boot/pprof/pprof_state_autogen.go
new file mode 100644
index 000000000..cabd43173
--- /dev/null
+++ b/runsc/boot/pprof/pprof_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package pprof