Enable SubprocessExited and SubprocessZombie for gVisor

Updates #164

PiperOrigin-RevId: 305544029
This commit is contained in:
Fabricio Voznika 2020-04-08 13:33:44 -07:00 committed by gVisor bot
parent 71c7e24e5c
commit b30130567d
4 changed files with 90 additions and 41 deletions

View File

@ -57,6 +57,16 @@ func getTaskMM(t *kernel.Task) (*mm.MemoryManager, error) {
return m, nil
}
func checkTaskState(t *kernel.Task) error {
switch t.ExitState() {
case kernel.TaskExitZombie:
return syserror.EACCES
case kernel.TaskExitDead:
return syserror.ESRCH
}
return nil
}
// taskDir represents a task-level directory.
//
// +stateify savable
@ -254,11 +264,12 @@ func newExe(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
}
func (e *exe) executable() (file fsbridge.File, err error) {
if err := checkTaskState(e.t); err != nil {
return nil, err
}
e.t.WithMuLocked(func(t *kernel.Task) {
mm := t.MemoryManager()
if mm == nil {
// TODO(b/34851096): Check shouldn't allow Readlink once the
// Task is zombied.
err = syserror.EACCES
return
}
@ -268,7 +279,7 @@ func (e *exe) executable() (file fsbridge.File, err error) {
// (with locks held).
file = mm.Executable()
if file == nil {
err = syserror.ENOENT
err = syserror.ESRCH
}
})
return
@ -313,11 +324,22 @@ func newNamespaceSymlink(t *kernel.Task, msrc *fs.MountSource, name string) *fs.
return newProcInode(t, n, msrc, fs.Symlink, t)
}
// Readlink reads the symlink value.
func (n *namespaceSymlink) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
if err := checkTaskState(n.t); err != nil {
return "", err
}
return n.Symlink.Readlink(ctx, inode)
}
// Getlink implements fs.InodeOperations.Getlink.
func (n *namespaceSymlink) Getlink(ctx context.Context, inode *fs.Inode) (*fs.Dirent, error) {
if !kernel.ContextCanTrace(ctx, n.t, false) {
return nil, syserror.EACCES
}
if err := checkTaskState(n.t); err != nil {
return nil, err
}
// Create a new regular file to fake the namespace file.
iops := fsutil.NewNoReadWriteFileInode(ctx, fs.RootOwner, fs.FilePermsFromMode(0777), linux.PROC_SUPER_MAGIC)

View File

@ -214,22 +214,6 @@ func newIO(t *kernel.Task, isThreadGroup bool) *ioData {
return &ioData{ioUsage: t}
}
func newNamespaceSymlink(task *kernel.Task, ino uint64, ns string) *kernfs.Dentry {
// Namespace symlinks should contain the namespace name and the inode number
// for the namespace instance, so for example user:[123456]. We currently fake
// the inode number by sticking the symlink inode in its place.
target := fmt.Sprintf("%s:[%d]", ns, ino)
inode := &kernfs.StaticSymlink{}
// Note: credentials are overridden by taskOwnedInode.
inode.Init(task.Credentials(), ino, target)
taskInode := &taskOwnedInode{Inode: inode, owner: task}
d := &kernfs.Dentry{}
d.Init(taskInode)
return d
}
// newCgroupData creates inode that shows cgroup information.
// From man 7 cgroups: "For each cgroup hierarchy of which the process is a
// member, there is one entry containing three colon-separated fields:

View File

@ -64,6 +64,16 @@ func getMMIncRef(task *kernel.Task) (*mm.MemoryManager, error) {
return m, nil
}
func checkTaskState(t *kernel.Task) error {
switch t.ExitState() {
case kernel.TaskExitZombie:
return syserror.EACCES
case kernel.TaskExitDead:
return syserror.ESRCH
}
return nil
}
type bufferWriter struct {
buf *bytes.Buffer
}
@ -628,11 +638,13 @@ func (s *exeSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, er
}
func (s *exeSymlink) executable() (file fsbridge.File, err error) {
if err := checkTaskState(s.task); err != nil {
return nil, err
}
s.task.WithMuLocked(func(t *kernel.Task) {
mm := t.MemoryManager()
if mm == nil {
// TODO(b/34851096): Check shouldn't allow Readlink once the
// Task is zombied.
err = syserror.EACCES
return
}
@ -642,7 +654,7 @@ func (s *exeSymlink) executable() (file fsbridge.File, err error) {
// (with locks held).
file = mm.Executable()
if file == nil {
err = syserror.ENOENT
err = syserror.ESRCH
}
})
return
@ -709,3 +721,41 @@ func (i *mountsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
i.task.Kernel().VFS().GenerateProcMounts(ctx, rootDir, buf)
return nil
}
type namespaceSymlink struct {
kernfs.StaticSymlink
task *kernel.Task
}
func newNamespaceSymlink(task *kernel.Task, ino uint64, ns string) *kernfs.Dentry {
// Namespace symlinks should contain the namespace name and the inode number
// for the namespace instance, so for example user:[123456]. We currently fake
// the inode number by sticking the symlink inode in its place.
target := fmt.Sprintf("%s:[%d]", ns, ino)
inode := &namespaceSymlink{task: task}
// Note: credentials are overridden by taskOwnedInode.
inode.Init(task.Credentials(), ino, target)
taskInode := &taskOwnedInode{Inode: inode, owner: task}
d := &kernfs.Dentry{}
d.Init(taskInode)
return d
}
// Readlink implements Inode.
func (s *namespaceSymlink) Readlink(ctx context.Context) (string, error) {
if err := checkTaskState(s.task); err != nil {
return "", err
}
return s.StaticSymlink.Readlink(ctx)
}
// Getlink implements Inode.Getlink.
func (s *namespaceSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
if err := checkTaskState(s.task); err != nil {
return vfs.VirtualDentry{}, "", err
}
return s.StaticSymlink.Getlink(ctx)
}

View File

@ -1326,8 +1326,6 @@ TEST(ProcPidSymlink, SubprocessRunning) {
SyscallSucceedsWithValue(sizeof(buf)));
}
// FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux
// on proc files.
TEST(ProcPidSymlink, SubprocessZombied) {
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));
@ -1337,7 +1335,7 @@ TEST(ProcPidSymlink, SubprocessZombied) {
int want = EACCES;
if (!IsRunningOnGvisor()) {
auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion());
if (version.major == 4 && version.minor > 3) {
if (version.major > 4 || (version.major == 4 && version.minor > 3)) {
want = ENOENT;
}
}
@ -1350,30 +1348,25 @@ TEST(ProcPidSymlink, SubprocessZombied) {
SyscallFailsWithErrno(want));
}
// FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux
// on proc files.
// FIXME(gvisor.dev/issue/164): Inconsistent behavior between linux on proc
// files.
//
// ~4.3: Syscall fails with EACCES.
// 4.17 & gVisor: Syscall succeeds and returns 1.
// 4.17: Syscall succeeds and returns 1.
//
// EXPECT_THAT(ReadlinkWhileZombied("ns/pid", buf, sizeof(buf)),
// SyscallFailsWithErrno(EACCES));
if (!IsRunningOnGvisor()) {
return;
}
// FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux
// on proc files.
//
// ~4.3: Syscall fails with EACCES.
// 4.17 & gVisor: Syscall succeeds and returns 1.
//
// EXPECT_THAT(ReadlinkWhileZombied("ns/user", buf, sizeof(buf)),
// SyscallFailsWithErrno(EACCES));
EXPECT_THAT(ReadlinkWhileZombied("ns/pid", buf, sizeof(buf)),
SyscallFailsWithErrno(want));
EXPECT_THAT(ReadlinkWhileZombied("ns/user", buf, sizeof(buf)),
SyscallFailsWithErrno(want));
}
// Test whether /proc/PID/ symlinks can be read for an exited process.
TEST(ProcPidSymlink, SubprocessExited) {
// FIXME(gvisor.dev/issue/164): These all succeed on gVisor.
SKIP_IF(IsRunningOnGvisor());
char buf[1];
EXPECT_THAT(ReadlinkWhileExited("exe", buf, sizeof(buf)),