Enable SubprocessExited and SubprocessZombie for gVisor
Updates #164 PiperOrigin-RevId: 305544029
This commit is contained in:
parent
71c7e24e5c
commit
b30130567d
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
|
|
Loading…
Reference in New Issue