summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKevin Krakauer <krakauer@google.com>2018-08-24 14:41:38 -0700
committerShentubot <shentubot@google.com>2018-08-24 14:42:40 -0700
commit02dfceab6d4c4a2a3342ef69be0265b7ab03e5d7 (patch)
tree31a84af980c995689395641e2a1cc2b5281e9e02
parent7b0dfb0cdbdcb402c000d30399dbfd2eeebe1266 (diff)
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
-rw-r--r--runsc/boot/fs.go45
-rw-r--r--runsc/boot/loader.go4
-rw-r--r--runsc/specutils/specutils.go25
3 files changed, 64 insertions, 10 deletions
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) {