Add basic implementation of execveat syscall and associated tests.

Allow file descriptors of directories as well as AT_FDCWD.

PiperOrigin-RevId: 275929668
This commit is contained in:
Dean Deng
2019-10-21 14:55:18 -07:00
committed by gVisor bot
parent a9358ce81f
commit 0b569b7cae
6 changed files with 220 additions and 85 deletions
+1 -1
View File
@@ -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.ErrorWithEvent("execveat", syserror.ENOSYS, "", []string{"gvisor.dev/issue/265"}), // TODO(b/118901836)
322: syscalls.PartiallySupported("execveat", Execveat, "No support for AT_EMPTY_PATH, 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),
+44 -4
View File
@@ -15,10 +15,12 @@
package linux
import (
"path"
"syscall"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/sched"
"gvisor.dev/gvisor/pkg/sentry/usermem"
@@ -67,8 +69,22 @@ func Execve(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
argvAddr := args[1].Pointer()
envvAddr := args[2].Pointer()
// Extract our arguments.
filename, err := t.CopyInString(filenameAddr, linux.PATH_MAX)
return execveat(t, linux.AT_FDCWD, filenameAddr, argvAddr, envvAddr, 0)
}
// Execveat implements linux syscall execveat(2).
func Execveat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
dirFD := args[0].Int()
pathnameAddr := args[1].Pointer()
argvAddr := args[2].Pointer()
envvAddr := args[3].Pointer()
flags := args[4].Int()
return execveat(t, dirFD, pathnameAddr, argvAddr, envvAddr, flags)
}
func execveat(t *kernel.Task, dirFD int32, pathnameAddr, argvAddr, envvAddr usermem.Addr, flags int32) (uintptr, *kernel.SyscallControl, error) {
pathname, err := t.CopyInString(pathnameAddr, linux.PATH_MAX)
if err != nil {
return 0, nil, err
}
@@ -89,14 +105,38 @@ func Execve(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
}
}
if flags != 0 {
// TODO(b/128449944): Handle AT_EMPTY_PATH and AT_SYMLINK_NOFOLLOW.
t.Kernel().EmitUnimplementedEvent(t)
return 0, nil, syserror.ENOSYS
}
root := t.FSContext().RootDirectory()
defer root.DecRef()
wd := t.FSContext().WorkingDirectory()
var wd *fs.Dirent
if dirFD == linux.AT_FDCWD || path.IsAbs(pathname) {
// If pathname is absolute, LoadTaskImage() will ignore the wd.
wd = t.FSContext().WorkingDirectory()
} else {
// Need to extract the given FD.
f := t.GetFile(dirFD)
if f == nil {
return 0, nil, syserror.EBADF
}
defer f.DecRef()
wd = f.Dirent
wd.IncRef()
if !fs.IsDir(wd.Inode.StableAttr) {
return 0, nil, syserror.ENOTDIR
}
}
defer wd.DecRef()
// Load the new TaskContext.
maxTraversals := uint(linux.MaxSymlinkTraversals)
tc, se := t.Kernel().LoadTaskImage(t, t.MountNamespace(), root, wd, &maxTraversals, filename, nil, argv, envv, t.Arch().FeatureSet())
tc, se := t.Kernel().LoadTaskImage(t, t.MountNamespace(), root, wd, &maxTraversals, pathname, nil, argv, envv, t.Arch().FeatureSet())
if se != nil {
return 0, nil, se.ToError()
}
+1
View File
@@ -670,6 +670,7 @@ cc_binary(
"//test/util:thread_util",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/types:optional",
"@com_google_googletest//:gtest",
],
)
+127 -74
View File
@@ -33,6 +33,7 @@
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/types/optional.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/multiprocess_util.h"
@@ -68,11 +69,12 @@ constexpr char kExit42[] = "--exec_exit_42";
constexpr char kExecWithThread[] = "--exec_exec_with_thread";
constexpr char kExecFromThread[] = "--exec_exec_from_thread";
// Runs filename with argv and checks that the exit status is expect_status and
// that stderr contains expect_stderr.
void CheckOutput(const std::string& filename, const ExecveArray& argv,
const ExecveArray& envv, int expect_status,
const std::string& expect_stderr) {
// Runs file specified by dirfd and pathname with argv and checks that the exit
// status is expect_status and that stderr contains expect_stderr.
void CheckExecHelper(const absl::optional<int32_t> dirfd,
const std::string& pathname, const ExecveArray& argv,
const ExecveArray& envv, const int flags,
int expect_status, const std::string& expect_stderr) {
int pipe_fds[2];
ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds());
@@ -110,8 +112,15 @@ void CheckOutput(const std::string& filename, const ExecveArray& argv,
// CloexecEventfd depend on that not happening.
};
auto kill = ASSERT_NO_ERRNO_AND_VALUE(
ForkAndExec(filename, argv, envv, remap_stderr, &child, &execve_errno));
Cleanup kill;
if (dirfd.has_value()) {
kill = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(*dirfd, pathname, argv,
envv, flags, remap_stderr,
&child, &execve_errno));
} else {
kill = ASSERT_NO_ERRNO_AND_VALUE(
ForkAndExec(pathname, argv, envv, remap_stderr, &child, &execve_errno));
}
ASSERT_EQ(0, execve_errno);
@@ -140,6 +149,21 @@ void CheckOutput(const std::string& filename, const ExecveArray& argv,
EXPECT_TRUE(absl::StrContains(output, expect_stderr)) << output;
}
void CheckExec(const std::string& filename, const ExecveArray& argv,
const ExecveArray& envv, int expect_status,
const std::string& expect_stderr) {
CheckExecHelper(/*dirfd=*/absl::optional<int32_t>(), filename, argv, envv,
/*flags=*/0, expect_status, expect_stderr);
}
void CheckExecveat(const int32_t dirfd, const std::string& pathname,
const ExecveArray& argv, const ExecveArray& envv,
const int flags, int expect_status,
const std::string& expect_stderr) {
CheckExecHelper(absl::optional<int32_t>(dirfd), pathname, argv, envv, flags,
expect_status, expect_stderr);
}
TEST(ExecTest, EmptyPath) {
int execve_errno;
ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec("", {}, {}, nullptr, &execve_errno));
@@ -147,46 +171,45 @@ TEST(ExecTest, EmptyPath) {
}
TEST(ExecTest, Basic) {
CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)}, {},
ArgEnvExitStatus(0, 0),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n"));
CheckExec(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)}, {},
ArgEnvExitStatus(0, 0),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n"));
}
TEST(ExecTest, OneArg) {
CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload), "1"},
{}, ArgEnvExitStatus(1, 0),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n"));
CheckExec(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload), "1"},
{}, ArgEnvExitStatus(1, 0),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n"));
}
TEST(ExecTest, FiveArg) {
CheckOutput(WorkloadPath(kBasicWorkload),
{WorkloadPath(kBasicWorkload), "1", "2", "3", "4", "5"}, {},
ArgEnvExitStatus(5, 0),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n2\n3\n4\n5\n"));
CheckExec(WorkloadPath(kBasicWorkload),
{WorkloadPath(kBasicWorkload), "1", "2", "3", "4", "5"}, {},
ArgEnvExitStatus(5, 0),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n2\n3\n4\n5\n"));
}
TEST(ExecTest, OneEnv) {
CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)},
{"1"}, ArgEnvExitStatus(0, 1),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n"));
CheckExec(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)}, {"1"},
ArgEnvExitStatus(0, 1),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n"));
}
TEST(ExecTest, FiveEnv) {
CheckOutput(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)},
{"1", "2", "3", "4", "5"}, ArgEnvExitStatus(0, 5),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n2\n3\n4\n5\n"));
CheckExec(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload)},
{"1", "2", "3", "4", "5"}, ArgEnvExitStatus(0, 5),
absl::StrCat(WorkloadPath(kBasicWorkload), "\n1\n2\n3\n4\n5\n"));
}
TEST(ExecTest, OneArgOneEnv) {
CheckOutput(WorkloadPath(kBasicWorkload),
{WorkloadPath(kBasicWorkload), "arg"}, {"env"},
ArgEnvExitStatus(1, 1),
absl::StrCat(WorkloadPath(kBasicWorkload), "\narg\nenv\n"));
CheckExec(WorkloadPath(kBasicWorkload), {WorkloadPath(kBasicWorkload), "arg"},
{"env"}, ArgEnvExitStatus(1, 1),
absl::StrCat(WorkloadPath(kBasicWorkload), "\narg\nenv\n"));
}
TEST(ExecTest, InterpreterScript) {
CheckOutput(WorkloadPath(kExitScript), {WorkloadPath(kExitScript), "25"}, {},
ArgEnvExitStatus(25, 0), "");
CheckExec(WorkloadPath(kExitScript), {WorkloadPath(kExitScript), "25"}, {},
ArgEnvExitStatus(25, 0), "");
}
// Everything after the path in the interpreter script is a single argument.
@@ -199,8 +222,8 @@ TEST(ExecTest, InterpreterScriptArgSplit) {
GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo bar"),
0755));
CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0),
absl::StrCat(link.path(), "\nfoo bar\n", script.path(), "\n"));
CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0),
absl::StrCat(link.path(), "\nfoo bar\n", script.path(), "\n"));
}
// Original argv[0] is replaced with the script path.
@@ -212,8 +235,8 @@ TEST(ExecTest, InterpreterScriptArgvZero) {
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755));
CheckOutput(script.path(), {"REPLACED"}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script.path(), "\n"));
CheckExec(script.path(), {"REPLACED"}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script.path(), "\n"));
}
// Original argv[0] is replaced with the script path, exactly as passed to
@@ -230,8 +253,8 @@ TEST(ExecTest, InterpreterScriptArgvZeroRelative) {
auto script_relative =
ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path()));
CheckOutput(script_relative, {"REPLACED"}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script_relative, "\n"));
CheckExec(script_relative, {"REPLACED"}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script_relative, "\n"));
}
// argv[0] is added as the script path, even if there was none.
@@ -243,8 +266,8 @@ TEST(ExecTest, InterpreterScriptArgvZeroAdded) {
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755));
CheckOutput(script.path(), {}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script.path(), "\n"));
CheckExec(script.path(), {}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script.path(), "\n"));
}
// A NUL byte in the script line ends parsing.
@@ -258,8 +281,8 @@ TEST(ExecTest, InterpreterScriptArgNUL) {
absl::StrCat("#!", link.path(), " foo", std::string(1, '\0'), "bar"),
0755));
CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0),
absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n"));
CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0),
absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n"));
}
// Trailing whitespace following interpreter path is ignored.
@@ -271,8 +294,8 @@ TEST(ExecTest, InterpreterScriptTrailingWhitespace) {
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " "), 0755));
CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script.path(), "\n"));
CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script.path(), "\n"));
}
// Multiple whitespace characters between interpreter and arg allowed.
@@ -284,8 +307,8 @@ TEST(ExecTest, InterpreterScriptArgWhitespace) {
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo"), 0755));
CheckOutput(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0),
absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n"));
CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0),
absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n"));
}
TEST(ExecTest, InterpreterScriptNoPath) {
@@ -314,15 +337,15 @@ TEST(ExecTest, ExecFn) {
auto script_relative =
ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path()));
CheckOutput(script_relative, {script_relative}, {}, ArgEnvExitStatus(0, 0),
absl::StrCat(script_relative, "\n"));
CheckExec(script_relative, {script_relative}, {}, ArgEnvExitStatus(0, 0),
absl::StrCat(script_relative, "\n"));
}
TEST(ExecTest, ExecName) {
std::string path = WorkloadPath(kStateWorkload);
CheckOutput(path, {path, "PrintExecName"}, {}, ArgEnvExitStatus(0, 0),
absl::StrCat(Basename(path).substr(0, 15), "\n"));
CheckExec(path, {path, "PrintExecName"}, {}, ArgEnvExitStatus(0, 0),
absl::StrCat(Basename(path).substr(0, 15), "\n"));
}
TEST(ExecTest, ExecNameScript) {
@@ -336,21 +359,21 @@ TEST(ExecTest, ExecNameScript) {
std::string script_path = script.path();
CheckOutput(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0),
absl::StrCat(Basename(script_path).substr(0, 15), "\n"));
CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0),
absl::StrCat(Basename(script_path).substr(0, 15), "\n"));
}
// execve may be called by a multithreaded process.
TEST(ExecTest, WithSiblingThread) {
CheckOutput("/proc/self/exe", {"/proc/self/exe", kExecWithThread}, {},
W_EXITCODE(42, 0), "");
CheckExec("/proc/self/exe", {"/proc/self/exe", kExecWithThread}, {},
W_EXITCODE(42, 0), "");
}
// execve may be called from a thread other than the leader of a multithreaded
// process.
TEST(ExecTest, FromSiblingThread) {
CheckOutput("/proc/self/exe", {"/proc/self/exe", kExecFromThread}, {},
W_EXITCODE(42, 0), "");
CheckExec("/proc/self/exe", {"/proc/self/exe", kExecFromThread}, {},
W_EXITCODE(42, 0), "");
}
TEST(ExecTest, NotFound) {
@@ -388,7 +411,7 @@ TEST(ExecStateTest, HandlerReset) {
absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_DFL))),
};
CheckOutput(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), "");
CheckExec(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), "");
}
// Ignored signal dispositions are not reset.
@@ -404,7 +427,7 @@ TEST(ExecStateTest, IgnorePreserved) {
absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_IGN))),
};
CheckOutput(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), "");
CheckExec(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), "");
}
// Signal masks are not reset on exec
@@ -420,7 +443,7 @@ TEST(ExecStateTest, SignalMask) {
absl::StrCat(SIGUSR1),
};
CheckOutput(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), "");
CheckExec(WorkloadPath(kStateWorkload), args, {}, W_EXITCODE(0, 0), "");
}
// itimers persist across execve.
@@ -472,10 +495,10 @@ TEST(ExecStateTest, ItimerPreserved) {
TEST(ProcSelfExe, ChangesAcrossExecve) {
// See exec_proc_exe_workload for more details. We simply
// assert that the /proc/self/exe link changes across execve.
CheckOutput(WorkloadPath(kProcExeWorkload),
{WorkloadPath(kProcExeWorkload),
ASSERT_NO_ERRNO_AND_VALUE(ProcessExePath(getpid()))},
{}, W_EXITCODE(0, 0), "");
CheckExec(WorkloadPath(kProcExeWorkload),
{WorkloadPath(kProcExeWorkload),
ASSERT_NO_ERRNO_AND_VALUE(ProcessExePath(getpid()))},
{}, W_EXITCODE(0, 0), "");
}
TEST(ExecTest, CloexecNormalFile) {
@@ -484,20 +507,20 @@ TEST(ExecTest, CloexecNormalFile) {
const FileDescriptor fd_closed_on_exec =
ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC));
CheckOutput(WorkloadPath(kAssertClosedWorkload),
{WorkloadPath(kAssertClosedWorkload),
absl::StrCat(fd_closed_on_exec.get())},
{}, W_EXITCODE(0, 0), "");
CheckExec(WorkloadPath(kAssertClosedWorkload),
{WorkloadPath(kAssertClosedWorkload),
absl::StrCat(fd_closed_on_exec.get())},
{}, W_EXITCODE(0, 0), "");
// The assert closed workload exits with code 2 if the file still exists. We
// can use this to do a negative test.
const FileDescriptor fd_open_on_exec =
ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY));
CheckOutput(WorkloadPath(kAssertClosedWorkload),
{WorkloadPath(kAssertClosedWorkload),
absl::StrCat(fd_open_on_exec.get())},
{}, W_EXITCODE(2, 0), "");
CheckExec(WorkloadPath(kAssertClosedWorkload),
{WorkloadPath(kAssertClosedWorkload),
absl::StrCat(fd_open_on_exec.get())},
{}, W_EXITCODE(2, 0), "");
}
TEST(ExecTest, CloexecEventfd) {
@@ -505,9 +528,40 @@ TEST(ExecTest, CloexecEventfd) {
ASSERT_THAT(efd = eventfd(0, EFD_CLOEXEC), SyscallSucceeds());
FileDescriptor fd(efd);
CheckOutput(WorkloadPath(kAssertClosedWorkload),
{WorkloadPath(kAssertClosedWorkload), absl::StrCat(fd.get())}, {},
W_EXITCODE(0, 0), "");
CheckExec(WorkloadPath(kAssertClosedWorkload),
{WorkloadPath(kAssertClosedWorkload), absl::StrCat(fd.get())}, {},
W_EXITCODE(0, 0), "");
}
TEST(ExecveatTest, BasicWithFDCWD) {
std::string path = WorkloadPath(kBasicWorkload);
CheckExecveat(AT_FDCWD, path, {path}, {}, /*flags=*/0, ArgEnvExitStatus(0, 0),
absl::StrCat(path, "\n"));
}
TEST(ExecveatTest, Basic) {
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}, {}, /*flags=*/0,
ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n"));
}
TEST(ExecveatTest, AbsolutePathWithFDCWD) {
std::string path = WorkloadPath(kBasicWorkload);
CheckExecveat(AT_FDCWD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0,
absl::StrCat(path, "\n"));
}
TEST(ExecveatTest, AbsolutePath) {
std::string path = WorkloadPath(kBasicWorkload);
// File descriptor should be ignored when an absolute path is given.
const int32_t badFD = -1;
CheckExecveat(badFD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0,
absl::StrCat(path, "\n"));
}
// Priority consistent across calls to execve()
@@ -522,9 +576,8 @@ TEST(GetpriorityTest, ExecveMaintainsPriority) {
// Program run (priority_execve) will exit(X) where
// X=getpriority(PRIO_PROCESS,0). Check that this exit value is prio.
CheckOutput(WorkloadPath(kPriorityWorkload),
{WorkloadPath(kPriorityWorkload)}, {},
W_EXITCODE(expected_exit_code, 0), "");
CheckExec(WorkloadPath(kPriorityWorkload), {WorkloadPath(kPriorityWorkload)},
{}, W_EXITCODE(expected_exit_code, 0), "");
}
void ExecWithThread() {
+40 -6
View File
@@ -14,6 +14,7 @@
#include "test/util/multiprocess_util.h"
#include <asm/unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
@@ -30,11 +31,12 @@
namespace gvisor {
namespace testing {
PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
const ExecveArray& argv,
const ExecveArray& envv,
const std::function<void()>& fn, pid_t* child,
int* execve_errno) {
namespace {
// exec_fn wraps a variant of the exec family, e.g. execve or execveat.
PosixErrorOr<Cleanup> ForkAndExecHelper(const std::function<void()>& exec_fn,
const std::function<void()>& fn,
pid_t* child, int* execve_errno) {
int pfds[2];
int ret = pipe2(pfds, O_CLOEXEC);
if (ret < 0) {
@@ -76,7 +78,9 @@ PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
fn();
}
execve(filename.c_str(), argv.get(), envv.get());
// Call variant of exec function.
exec_fn();
int error = errno;
if (WriteFd(pfds[1], &error, sizeof(error)) != sizeof(error)) {
// We can't do much if the write fails, but we can at least exit with a
@@ -116,6 +120,36 @@ PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
return std::move(cleanup);
}
} // namespace
PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
const ExecveArray& argv,
const ExecveArray& envv,
const std::function<void()>& fn, pid_t* child,
int* execve_errno) {
char* const* argv_data = argv.get();
char* const* envv_data = envv.get();
const std::function<void()> exec_fn = [=] {
execve(filename.c_str(), argv_data, envv_data);
};
return ForkAndExecHelper(exec_fn, fn, child, execve_errno);
}
PosixErrorOr<Cleanup> ForkAndExecveat(const int32_t dirfd,
const std::string& pathname,
const ExecveArray& argv,
const ExecveArray& envv, const int flags,
const std::function<void()>& fn,
pid_t* child, int* execve_errno) {
char* const* argv_data = argv.get();
char* const* envv_data = envv.get();
const std::function<void()> exec_fn = [=] {
syscall(__NR_execveat, dirfd, pathname.c_str(), argv_data, envv_data,
flags);
};
return ForkAndExecHelper(exec_fn, fn, child, execve_errno);
}
PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn) {
pid_t pid = fork();
if (pid == 0) {
+7
View File
@@ -102,6 +102,13 @@ inline PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
return ForkAndExec(filename, argv, envv, [] {}, child, execve_errno);
}
// Equivalent to ForkAndExec, except using dirfd and flags with execveat.
PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, const std::string& pathname,
const ExecveArray& argv,
const ExecveArray& envv, int flags,
const std::function<void()>& fn,
pid_t* child, int* execve_errno);
// Calls fn in a forked subprocess and returns the exit status of the
// subprocess.
//