diff --git a/pkg/sentry/fsimpl/pipefs/pipefs.go b/pkg/sentry/fsimpl/pipefs/pipefs.go index 6e26ad2b2..6993fe065 100644 --- a/pkg/sentry/fsimpl/pipefs/pipefs.go +++ b/pkg/sentry/fsimpl/pipefs/pipefs.go @@ -161,10 +161,19 @@ func (i *inode) Stat(_ context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOpti // SetStat implements kernfs.Inode.SetStat. func (i *inode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error { - if opts.Stat.Mask == 0 { - return nil + if opts.Stat.Mask&^(linux.STATX_UID|linux.STATX_GID) != 0 { + return linuxerr.EPERM } - return linuxerr.EPERM + if err := vfs.CheckSetStat(ctx, creds, &opts, i.Mode(), i.UID(), i.GID()); err != nil { + return err + } + if opts.Stat.Mask&linux.STATX_UID != 0 { + i.uid = auth.KUID(opts.Stat.UID) + } + if opts.Stat.Mask&linux.STATX_GID != 0 { + i.gid = auth.KGID(opts.Stat.GID) + } + return nil } // Open implements kernfs.Inode.Open. diff --git a/test/syscalls/linux/chown.cc b/test/syscalls/linux/chown.cc index 29620ecf7..0f90f4d47 100644 --- a/test/syscalls/linux/chown.cc +++ b/test/syscalls/linux/chown.cc @@ -65,6 +65,43 @@ TEST(ChownTest, FchownDirWithOpath) { SyscallFailsWithErrno(EBADF)); } +TEST(ChownTest, FchownPipeFileSucceeds) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_CHOWN))); + const auto uid = absl::GetFlag(FLAGS_scratch_uid1); + const auto gid = absl::GetFlag(FLAGS_scratch_gid); + int fds[2]; + ASSERT_THAT(pipe2(fds, O_CLOEXEC), SyscallSucceeds()); + for (const auto& fd : fds) { + ASSERT_THAT(fchown(fd, uid, gid), SyscallSucceeds()); + struct stat s = {}; + ASSERT_THAT(fstat(fd, &s), SyscallSucceeds()); + EXPECT_EQ(s.st_uid, uid); + EXPECT_EQ(s.st_gid, gid); + } +} + +TEST(ChownTest, FchownPipeFileFails) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + int fds[2]; + ASSERT_THAT(pipe2(fds, O_CLOEXEC), SyscallSucceeds()); + ScopedThread([&] { + // Drop privileges. + AutoCapability cap(CAP_CHOWN, false); + + // Change EUID and EGID. + // + // See note about POSIX below. + EXPECT_THAT( + syscall(SYS_setresgid, -1, absl::GetFlag(FLAGS_scratch_gid), -1), + SyscallSucceeds()); + EXPECT_THAT( + syscall(SYS_setresuid, -1, absl::GetFlag(FLAGS_scratch_uid1), -1), + SyscallSucceeds()); + EXPECT_THAT(fchown(fds[1], geteuid(), getegid()), + SyscallFailsWithErrno(EPERM)); + }); +} + TEST(ChownTest, FchownatWithOpath) { const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path()));