summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDean Deng <deandeng@google.com>2019-10-23 22:21:33 -0700
committergVisor bot <gvisor-bot@google.com>2019-10-23 22:23:05 -0700
commit7ca50236c42ad1b1aa19951815d03b62c0c722ed (patch)
tree586fa422b8447eb441e58deb521c74884dad9a6d
parent072af49059a1818e0e06188be81fe425363acf55 (diff)
Handle AT_EMPTY_PATH flag in execveat.
PiperOrigin-RevId: 276419967
-rw-r--r--pkg/sentry/syscalls/linux/linux64_amd64.go2
-rw-r--r--pkg/sentry/syscalls/linux/sys_thread.go32
-rw-r--r--test/syscalls/linux/exec.cc62
-rw-r--r--test/util/multiprocess_util.h9
4 files changed, 95 insertions, 10 deletions
diff --git a/pkg/sentry/syscalls/linux/linux64_amd64.go b/pkg/sentry/syscalls/linux/linux64_amd64.go
index aedb6d774..6d3801ad9 100644
--- a/pkg/sentry/syscalls/linux/linux64_amd64.go
+++ b/pkg/sentry/syscalls/linux/linux64_amd64.go
@@ -362,7 +362,7 @@ var AMD64 = &kernel.SyscallTable{
319: syscalls.Supported("memfd_create", MemfdCreate),
320: syscalls.CapError("kexec_file_load", linux.CAP_SYS_BOOT, "", nil),
321: syscalls.CapError("bpf", linux.CAP_SYS_ADMIN, "", nil),
- 322: syscalls.PartiallySupported("execveat", Execveat, "No support for AT_EMPTY_PATH, AT_SYMLINK_FOLLOW.", nil),
+ 322: syscalls.PartiallySupported("execveat", Execveat, "No support for AT_SYMLINK_FOLLOW.", nil),
323: syscalls.ErrorWithEvent("userfaultfd", syserror.ENOSYS, "", []string{"gvisor.dev/issue/266"}), // TODO(b/118906345)
324: syscalls.ErrorWithEvent("membarrier", syserror.ENOSYS, "", []string{"gvisor.dev/issue/267"}), // TODO(b/118904897)
325: syscalls.PartiallySupported("mlock2", Mlock2, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go
index 6e425f1ec..7ece7ba6f 100644
--- a/pkg/sentry/syscalls/linux/sys_thread.go
+++ b/pkg/sentry/syscalls/linux/sys_thread.go
@@ -105,18 +105,26 @@ func execveat(t *kernel.Task, dirFD int32, pathnameAddr, argvAddr, envvAddr user
}
}
- if flags != 0 {
- // TODO(b/128449944): Handle AT_EMPTY_PATH and AT_SYMLINK_NOFOLLOW.
+ if flags&linux.AT_SYMLINK_NOFOLLOW != 0 {
+ // TODO(b/128449944): Handle AT_SYMLINK_NOFOLLOW.
t.Kernel().EmitUnimplementedEvent(t)
return 0, nil, syserror.ENOSYS
}
+ atEmptyPath := flags&linux.AT_EMPTY_PATH != 0
+ if !atEmptyPath && len(pathname) == 0 {
+ return 0, nil, syserror.ENOENT
+ }
+
root := t.FSContext().RootDirectory()
defer root.DecRef()
var wd *fs.Dirent
+ var executable *fs.File
if dirFD == linux.AT_FDCWD || path.IsAbs(pathname) {
- // If pathname is absolute, LoadTaskImage() will ignore the wd.
+ // Even if the pathname is absolute, we may still need the wd
+ // for interpreter scripts if the path of the interpreter is
+ // relative.
wd = t.FSContext().WorkingDirectory()
} else {
// Need to extract the given FD.
@@ -126,17 +134,23 @@ func execveat(t *kernel.Task, dirFD int32, pathnameAddr, argvAddr, envvAddr user
}
defer f.DecRef()
- wd = f.Dirent
- wd.IncRef()
- if !fs.IsDir(wd.Inode.StableAttr) {
- return 0, nil, syserror.ENOTDIR
+ if atEmptyPath && len(pathname) == 0 {
+ executable = f
+ } else {
+ wd = f.Dirent
+ wd.IncRef()
+ if !fs.IsDir(wd.Inode.StableAttr) {
+ return 0, nil, syserror.ENOTDIR
+ }
}
}
- defer wd.DecRef()
+ if wd != nil {
+ defer wd.DecRef()
+ }
// Load the new TaskContext.
maxTraversals := uint(linux.MaxSymlinkTraversals)
- tc, se := t.Kernel().LoadTaskImage(t, t.MountNamespace(), root, wd, &maxTraversals, pathname, nil, argv, envv, t.Arch().FeatureSet())
+ tc, se := t.Kernel().LoadTaskImage(t, t.MountNamespace(), root, wd, &maxTraversals, pathname, executable, argv, envv, t.Arch().FeatureSet())
if se != nil {
return 0, nil, se.ToError()
}
diff --git a/test/syscalls/linux/exec.cc b/test/syscalls/linux/exec.cc
index 85734c290..03ec9f75f 100644
--- a/test/syscalls/linux/exec.cc
+++ b/test/syscalls/linux/exec.cc
@@ -550,6 +550,18 @@ TEST(ExecveatTest, Basic) {
ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n"));
}
+TEST(ExecveatTest, FDNotADirectory) {
+ std::string absolute_path = WorkloadPath(kBasicWorkload);
+ std::string relative_path = std::string(Basename(absolute_path));
+ const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(absolute_path, 0));
+
+ int execve_errno;
+ ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(fd.get(), relative_path,
+ {absolute_path}, {}, /*flags=*/0,
+ /*child=*/nullptr, &execve_errno));
+ EXPECT_EQ(execve_errno, ENOTDIR);
+}
+
TEST(ExecveatTest, AbsolutePathWithFDCWD) {
std::string path = WorkloadPath(kBasicWorkload);
CheckExecveat(AT_FDCWD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0,
@@ -564,6 +576,56 @@ TEST(ExecveatTest, AbsolutePath) {
absl::StrCat(path, "\n"));
}
+TEST(ExecveatTest, EmptyPathBasic) {
+ std::string path = WorkloadPath(kBasicWorkload);
+ const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH));
+
+ CheckExecveat(fd.get(), "", {path}, {}, AT_EMPTY_PATH, ArgEnvExitStatus(0, 0),
+ absl::StrCat(path, "\n"));
+}
+
+TEST(ExecveatTest, EmptyPathWithDirFD) {
+ std::string path = WorkloadPath(kBasicWorkload);
+ std::string parent_dir = std::string(Dirname(path));
+ const FileDescriptor dirfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY));
+
+ int execve_errno;
+ ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), "", {path}, {},
+ AT_EMPTY_PATH,
+ /*child=*/nullptr, &execve_errno));
+ EXPECT_EQ(execve_errno, EACCES);
+}
+
+TEST(ExecveatTest, EmptyPathWithoutEmptyPathFlag) {
+ std::string path = WorkloadPath(kBasicWorkload);
+ const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH));
+
+ int execve_errno;
+ ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(
+ fd.get(), "", {path}, {}, /*flags=*/0, /*child=*/nullptr, &execve_errno));
+ EXPECT_EQ(execve_errno, ENOENT);
+}
+
+TEST(ExecveatTest, AbsolutePathWithEmptyPathFlag) {
+ std::string path = WorkloadPath(kBasicWorkload);
+ const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH));
+
+ CheckExecveat(fd.get(), path, {path}, {}, AT_EMPTY_PATH,
+ ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n"));
+}
+
+TEST(ExecveatTest, RelativePathWithEmptyPathFlag) {
+ std::string absolute_path = WorkloadPath(kBasicWorkload);
+ std::string parent_dir = std::string(Dirname(absolute_path));
+ std::string relative_path = std::string(Basename(absolute_path));
+ const FileDescriptor dirfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY));
+
+ CheckExecveat(dirfd.get(), relative_path, {absolute_path}, {}, AT_EMPTY_PATH,
+ ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n"));
+}
+
// Priority consistent across calls to execve()
TEST(GetpriorityTest, ExecveMaintainsPriority) {
int prio = 16;
diff --git a/test/util/multiprocess_util.h b/test/util/multiprocess_util.h
index c413d63ea..61526b4e7 100644
--- a/test/util/multiprocess_util.h
+++ b/test/util/multiprocess_util.h
@@ -109,6 +109,15 @@ PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, const std::string& pathname
const std::function<void()>& fn,
pid_t* child, int* execve_errno);
+inline PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd,
+ const std::string& pathname,
+ const ExecveArray& argv,
+ const ExecveArray& envv, int flags,
+ pid_t* child, int* execve_errno) {
+ return ForkAndExecveat(
+ dirfd, pathname, argv, envv, flags, [] {}, child, execve_errno);
+}
+
// Calls fn in a forked subprocess and returns the exit status of the
// subprocess.
//