mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Handle AT_EMPTY_PATH flag in execveat.
PiperOrigin-RevId: 276419967
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user