summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
Diffstat (limited to 'runsc')
-rw-r--r--runsc/boot/compat.go8
-rw-r--r--runsc/boot/compat_amd64.go22
-rw-r--r--runsc/boot/compat_arm64.go22
-rw-r--r--runsc/boot/fs.go22
-rw-r--r--runsc/boot/vfs.go90
-rw-r--r--runsc/cgroup/cgroup.go50
-rw-r--r--runsc/cmd/help.go12
-rw-r--r--runsc/container/container_test.go2
8 files changed, 165 insertions, 63 deletions
diff --git a/runsc/boot/compat.go b/runsc/boot/compat.go
index b7cfb35bf..84c67cbc2 100644
--- a/runsc/boot/compat.go
+++ b/runsc/boot/compat.go
@@ -119,7 +119,13 @@ func (c *compatEmitter) emitUnimplementedSyscall(us *spb.UnimplementedSyscall) {
}
if tr.shouldReport(regs) {
- c.sink.Infof("Unsupported syscall: %s, regs: %+v", c.nameMap.Name(uintptr(sysnr)), regs)
+ name := c.nameMap.Name(uintptr(sysnr))
+ c.sink.Infof("Unsupported syscall %s(%#x,%#x,%#x,%#x,%#x,%#x). It is "+
+ "likely that you can safely ignore this message and that this is not "+
+ "the cause of any error. Please, refer to %s/%s for more information.",
+ name, argVal(0, regs), argVal(1, regs), argVal(2, regs), argVal(3, regs),
+ argVal(4, regs), argVal(5, regs), syscallLink, name)
+
tr.onReported(regs)
}
}
diff --git a/runsc/boot/compat_amd64.go b/runsc/boot/compat_amd64.go
index 42b0ca8b0..8eb76b2ba 100644
--- a/runsc/boot/compat_amd64.go
+++ b/runsc/boot/compat_amd64.go
@@ -24,8 +24,12 @@ import (
"gvisor.dev/gvisor/pkg/sentry/strace"
)
-// reportLimit is the max number of events that should be reported per tracker.
-const reportLimit = 100
+const (
+ // reportLimit is the max number of events that should be reported per
+ // tracker.
+ reportLimit = 100
+ syscallLink = "https://gvisor.dev/c/linux/amd64"
+)
// newRegs create a empty Registers instance.
func newRegs() *rpb.Registers {
@@ -36,22 +40,22 @@ func newRegs() *rpb.Registers {
}
}
-func argVal(argIdx int, regs *rpb.Registers) uint32 {
+func argVal(argIdx int, regs *rpb.Registers) uint64 {
amd64Regs := regs.GetArch().(*rpb.Registers_Amd64).Amd64
switch argIdx {
case 0:
- return uint32(amd64Regs.Rdi)
+ return amd64Regs.Rdi
case 1:
- return uint32(amd64Regs.Rsi)
+ return amd64Regs.Rsi
case 2:
- return uint32(amd64Regs.Rdx)
+ return amd64Regs.Rdx
case 3:
- return uint32(amd64Regs.R10)
+ return amd64Regs.R10
case 4:
- return uint32(amd64Regs.R8)
+ return amd64Regs.R8
case 5:
- return uint32(amd64Regs.R9)
+ return amd64Regs.R9
}
panic(fmt.Sprintf("invalid syscall argument index %d", argIdx))
}
diff --git a/runsc/boot/compat_arm64.go b/runsc/boot/compat_arm64.go
index f784cd237..bce9d95b3 100644
--- a/runsc/boot/compat_arm64.go
+++ b/runsc/boot/compat_arm64.go
@@ -23,8 +23,12 @@ import (
"gvisor.dev/gvisor/pkg/sentry/strace"
)
-// reportLimit is the max number of events that should be reported per tracker.
-const reportLimit = 100
+const (
+ // reportLimit is the max number of events that should be reported per
+ // tracker.
+ reportLimit = 100
+ syscallLink = "https://gvisor.dev/c/linux/arm64"
+)
// newRegs create a empty Registers instance.
func newRegs() *rpb.Registers {
@@ -35,22 +39,22 @@ func newRegs() *rpb.Registers {
}
}
-func argVal(argIdx int, regs *rpb.Registers) uint32 {
+func argVal(argIdx int, regs *rpb.Registers) uint64 {
arm64Regs := regs.GetArch().(*rpb.Registers_Arm64).Arm64
switch argIdx {
case 0:
- return uint32(arm64Regs.R0)
+ return arm64Regs.R0
case 1:
- return uint32(arm64Regs.R1)
+ return arm64Regs.R1
case 2:
- return uint32(arm64Regs.R2)
+ return arm64Regs.R2
case 3:
- return uint32(arm64Regs.R3)
+ return arm64Regs.R3
case 4:
- return uint32(arm64Regs.R4)
+ return arm64Regs.R4
case 5:
- return uint32(arm64Regs.R5)
+ return arm64Regs.R5
}
panic(fmt.Sprintf("invalid syscall argument index %d", argIdx))
}
diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go
index 8df5cc989..e1181271a 100644
--- a/runsc/boot/fs.go
+++ b/runsc/boot/fs.go
@@ -770,14 +770,8 @@ func (c *containerMounter) getMountNameAndOptions(conf *Config, m specs.Mount) (
useOverlay bool
)
- for _, opt := range m.Options {
- // When options include either "bind" or "rbind", this behaves as
- // bind mount even if the mount type is equal to a filesystem supported
- // on runsc.
- if opt == "bind" || opt == "rbind" {
- m.Type = bind
- break
- }
+ if isBindMount(m) {
+ m.Type = bind
}
switch m.Type {
@@ -807,6 +801,18 @@ func (c *containerMounter) getMountNameAndOptions(conf *Config, m specs.Mount) (
return fsName, opts, useOverlay, nil
}
+func isBindMount(m specs.Mount) bool {
+ for _, opt := range m.Options {
+ // When options include either "bind" or "rbind", this behaves as
+ // bind mount even if the mount type is equal to a filesystem supported
+ // on runsc.
+ if opt == "bind" || opt == "rbind" {
+ return true
+ }
+ }
+ return false
+}
+
func (c *containerMounter) getMountAccessType(mount specs.Mount) FileAccessType {
if hint := c.hints.findMount(mount); hint != nil {
return hint.fileAccessType()
diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go
index 7378fbc95..147c901c4 100644
--- a/runsc/boot/vfs.go
+++ b/runsc/boot/vfs.go
@@ -203,28 +203,61 @@ func (c *containerMounter) createMountNamespaceVFS2(ctx context.Context, conf *C
}
func (c *containerMounter) mountSubmountsVFS2(ctx context.Context, conf *Config, mns *vfs.MountNamespace, creds *auth.Credentials) error {
- c.prepareMountsVFS2()
+ mounts, err := c.prepareMountsVFS2()
+ if err != nil {
+ return err
+ }
- for _, submount := range c.mounts {
+ for i := range mounts {
+ submount := &mounts[i]
log.Debugf("Mounting %q to %q, type: %s, options: %s", submount.Source, submount.Destination, submount.Type, submount.Options)
- if err := c.mountSubmountVFS2(ctx, conf, mns, creds, &submount); err != nil {
+ if err := c.mountSubmountVFS2(ctx, conf, mns, creds, submount); err != nil {
return err
}
}
// TODO(gvisor.dev/issue/1487): implement mountTmp from fs.go.
- return c.checkDispenser()
+ return nil
+}
+
+type mountAndFD struct {
+ specs.Mount
+ fd int
}
-func (c *containerMounter) prepareMountsVFS2() {
+func (c *containerMounter) prepareMountsVFS2() ([]mountAndFD, error) {
+ // Associate bind mounts with their FDs before sorting since there is an
+ // undocumented assumption that FDs are dispensed in the order in which
+ // they are required by mounts.
+ var mounts []mountAndFD
+ for _, m := range c.mounts {
+ fd := -1
+ // Only bind mounts use host FDs; see
+ // containerMounter.getMountNameAndOptionsVFS2.
+ if m.Type == bind || isBindMount(m) {
+ fd = c.fds.remove()
+ }
+ mounts = append(mounts, mountAndFD{
+ Mount: m,
+ fd: fd,
+ })
+ }
+ if err := c.checkDispenser(); err != nil {
+ return nil, err
+ }
+
// Sort the mounts so that we don't place children before parents.
- sort.Slice(c.mounts, func(i, j int) bool { return len(c.mounts[i].Destination) < len(c.mounts[j].Destination) })
+ sort.Slice(mounts, func(i, j int) bool {
+ return len(mounts[i].Destination) < len(mounts[j].Destination)
+ })
+
+ return mounts, nil
}
// TODO(gvisor.dev/issue/1487): Implement submount options similar to the VFS1
-// version.
-func (c *containerMounter) mountSubmountVFS2(ctx context.Context, conf *Config, mns *vfs.MountNamespace, creds *auth.Credentials, submount *specs.Mount) error {
+// version.
+func (c *containerMounter) mountSubmountVFS2(ctx context.Context, conf *Config, mns *vfs.MountNamespace, creds *auth.Credentials, submount *mountAndFD) error {
root := mns.Root()
defer root.DecRef()
target := &vfs.PathOperation{
@@ -233,7 +266,7 @@ func (c *containerMounter) mountSubmountVFS2(ctx context.Context, conf *Config,
Path: fspath.Parse(submount.Destination),
}
- fsName, options, useOverlay, err := c.getMountNameAndOptions(conf, *submount)
+ fsName, options, useOverlay, err := c.getMountNameAndOptionsVFS2(conf, submount)
if err != nil {
return fmt.Errorf("mountOptions failed: %w", err)
}
@@ -263,6 +296,45 @@ func (c *containerMounter) mountSubmountVFS2(ctx context.Context, conf *Config,
return nil
}
+// getMountNameAndOptionsVFS2 retrieves the fsName, opts, and useOverlay values
+// used for mounts.
+func (c *containerMounter) getMountNameAndOptionsVFS2(conf *Config, m *mountAndFD) (string, []string, bool, error) {
+ var (
+ fsName string
+ opts []string
+ useOverlay bool
+ )
+
+ if isBindMount(m.Mount) {
+ m.Type = bind
+ }
+
+ switch m.Type {
+ case devpts.Name, devtmpfs.Name, proc.Name, sys.Name:
+ fsName = m.Type
+ case nonefs:
+ fsName = sys.Name
+ case tmpfs.Name:
+ fsName = m.Type
+
+ var err error
+ opts, err = parseAndFilterOptions(m.Options, tmpfsAllowedOptions...)
+ if err != nil {
+ return "", nil, false, err
+ }
+
+ case bind:
+ fsName = gofer.Name
+ opts = p9MountOptions(m.fd, c.getMountAccessType(m.Mount), true /* vfs2 */)
+ // If configured, add overlay to all writable mounts.
+ useOverlay = conf.Overlay && !mountFlags(m.Options).ReadOnly
+
+ default:
+ log.Warningf("ignoring unknown filesystem type %q", m.Type)
+ }
+ return fsName, opts, useOverlay, nil
+}
+
func (c *containerMounter) makeSyntheticMount(ctx context.Context, currentPath string, root vfs.VirtualDentry, creds *auth.Credentials) error {
target := &vfs.PathOperation{
Root: root,
diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go
index fa40ee509..19c8b0db6 100644
--- a/runsc/cgroup/cgroup.go
+++ b/runsc/cgroup/cgroup.go
@@ -19,6 +19,7 @@ package cgroup
import (
"bufio"
"context"
+ "errors"
"fmt"
"io/ioutil"
"os"
@@ -38,21 +39,23 @@ const (
cgroupRoot = "/sys/fs/cgroup"
)
-var controllers = map[string]controller{
- "blkio": &blockIO{},
- "cpu": &cpu{},
- "cpuset": &cpuSet{},
- "memory": &memory{},
- "net_cls": &networkClass{},
- "net_prio": &networkPrio{},
- "pids": &pids{},
+var controllers = map[string]config{
+ "blkio": config{ctrlr: &blockIO{}},
+ "cpu": config{ctrlr: &cpu{}},
+ "cpuset": config{ctrlr: &cpuSet{}},
+ "memory": config{ctrlr: &memory{}},
+ "net_cls": config{ctrlr: &networkClass{}},
+ "net_prio": config{ctrlr: &networkPrio{}},
+ "pids": config{ctrlr: &pids{}},
// These controllers either don't have anything in the OCI spec or is
// irrelevant for a sandbox.
- "devices": &noop{},
- "freezer": &noop{},
- "perf_event": &noop{},
- "systemd": &noop{},
+ "devices": config{ctrlr: &noop{}},
+ "freezer": config{ctrlr: &noop{}},
+ "hugetlb": config{ctrlr: &noop{}, optional: true},
+ "perf_event": config{ctrlr: &noop{}},
+ "rdma": config{ctrlr: &noop{}, optional: true},
+ "systemd": config{ctrlr: &noop{}},
}
func setOptionalValueInt(path, name string, val *int64) error {
@@ -196,8 +199,9 @@ func LoadPaths(pid string) (map[string]string, error) {
return paths, nil
}
-// Cgroup represents a group inside all controllers. For example: Name='/foo/bar'
-// maps to /sys/fs/cgroup/<controller>/foo/bar on all controllers.
+// Cgroup represents a group inside all controllers. For example:
+// Name='/foo/bar' maps to /sys/fs/cgroup/<controller>/foo/bar on
+// all controllers.
type Cgroup struct {
Name string `json:"name"`
Parents map[string]string `json:"parents"`
@@ -245,13 +249,17 @@ func (c *Cgroup) Install(res *specs.LinuxResources) error {
clean := specutils.MakeCleanup(func() { _ = c.Uninstall() })
defer clean.Clean()
- for key, ctrl := range controllers {
+ for key, cfg := range controllers {
path := c.makePath(key)
if err := os.MkdirAll(path, 0755); err != nil {
+ if cfg.optional && errors.Is(err, syscall.EROFS) {
+ log.Infof("Skipping cgroup %q", key)
+ continue
+ }
return err
}
if res != nil {
- if err := ctrl.set(res, path); err != nil {
+ if err := cfg.ctrlr.set(res, path); err != nil {
return err
}
}
@@ -321,10 +329,13 @@ func (c *Cgroup) Join() (func(), error) {
}
// Now join the cgroups.
- for key := range controllers {
+ for key, cfg := range controllers {
path := c.makePath(key)
log.Debugf("Joining cgroup %q", path)
if err := setValue(path, "cgroup.procs", "0"); err != nil {
+ if cfg.optional && os.IsNotExist(err) {
+ continue
+ }
return undo, err
}
}
@@ -375,6 +386,11 @@ func (c *Cgroup) makePath(controllerName string) string {
return filepath.Join(cgroupRoot, controllerName, path)
}
+type config struct {
+ ctrlr controller
+ optional bool
+}
+
type controller interface {
set(*specs.LinuxResources, string) error
}
diff --git a/runsc/cmd/help.go b/runsc/cmd/help.go
index c7d210140..cd85dabbb 100644
--- a/runsc/cmd/help.go
+++ b/runsc/cmd/help.go
@@ -65,16 +65,10 @@ func (h *Help) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}
switch f.NArg() {
case 0:
fmt.Fprintf(h.cdr.Output, "Usage: %s <flags> <subcommand> <subcommand args>\n\n", h.cdr.Name())
- fmt.Fprintf(h.cdr.Output, `runsc is a command line client for running applications packaged in the Open
-Container Initiative (OCI) format. Applications run by runsc are run in an
-isolated gVisor sandbox that emulates a Linux environment.
+ fmt.Fprintf(h.cdr.Output, `runsc is the gVisor container runtime.
-gVisor is a user-space kernel, written in Go, that implements a substantial
-portion of the Linux system call interface. It provides an additional layer
-of isolation between running applications and the host operating system.
-
-Functionality is provided by subcommands. For additonal help on individual
-subcommands use "%s %s <subcommand>".
+Functionality is provided by subcommands. For help with a specific subcommand,
+use "%s %s <subcommand>".
`, h.cdr.Name(), h.Name())
h.cdr.VisitGroups(func(g *subcommands.CommandGroup) {
diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go
index 7ba301331..1a6d50d0d 100644
--- a/runsc/container/container_test.go
+++ b/runsc/container/container_test.go
@@ -1760,7 +1760,7 @@ func TestUserLog(t *testing.T) {
if err != nil {
t.Fatalf("error opening user log file %q: %v", userLog, err)
}
- if want := "Unsupported syscall: sched_rr_get_interval"; !strings.Contains(string(out), want) {
+ if want := "Unsupported syscall sched_rr_get_interval("; !strings.Contains(string(out), want) {
t.Errorf("user log file doesn't contain %q, out: %s", want, string(out))
}
}