Skip to content

Commit d8c3302

Browse files
committed
Add O_PATH support in vfs2
PiperOrigin-RevId: 354367665
1 parent 449c155 commit d8c3302

36 files changed

+837
-18
lines changed

pkg/sentry/syscalls/linux/vfs2/fd.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
123123
}
124124
defer file.DecRef(t)
125125

126+
if file.StatusFlags()&linux.O_PATH != 0 {
127+
switch cmd {
128+
case linux.F_DUPFD, linux.F_DUPFD_CLOEXEC, linux.F_GETFD, linux.F_SETFD, linux.F_GETFL:
129+
// allowed
130+
default:
131+
return 0, nil, syserror.EBADF
132+
}
133+
}
134+
126135
switch cmd {
127136
case linux.F_DUPFD, linux.F_DUPFD_CLOEXEC:
128137
minfd := args[2].Int()
@@ -395,6 +404,10 @@ func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
395404
}
396405
defer file.DecRef(t)
397406

407+
if file.StatusFlags()&linux.O_PATH != 0 {
408+
return 0, nil, syserror.EBADF
409+
}
410+
398411
// If the FD refers to a pipe or FIFO, return error.
399412
if _, isPipe := file.Impl().(*pipe.VFSPipeFD); isPipe {
400413
return 0, nil, syserror.ESPIPE

pkg/sentry/syscalls/linux/vfs2/ioctl.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
3232
}
3333
defer file.DecRef(t)
3434

35+
if file.StatusFlags()&linux.O_PATH != 0 {
36+
return 0, nil, syserror.EBADF
37+
}
38+
3539
// Handle ioctls that apply to all FDs.
3640
switch args[1].Int() {
3741
case linux.FIONCLEX:

pkg/sentry/syscalls/linux/vfs2/sync.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ func Syncfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
3636
}
3737
defer file.DecRef(t)
3838

39+
if file.StatusFlags()&linux.O_PATH != 0 {
40+
return 0, nil, syserror.EBADF
41+
}
42+
3943
return 0, nil, file.SyncFS(t)
4044
}
4145

pkg/sentry/vfs/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ go_library(
8383
"mount.go",
8484
"mount_namespace_refs.go",
8585
"mount_unsafe.go",
86+
"opath.go",
8687
"options.go",
8788
"pathname.go",
8889
"permissions.go",

pkg/sentry/vfs/opath.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2019 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package vfs
16+
17+
import (
18+
"gvisor.dev/gvisor/pkg/abi/linux"
19+
"gvisor.dev/gvisor/pkg/context"
20+
"gvisor.dev/gvisor/pkg/sentry/arch"
21+
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
22+
"gvisor.dev/gvisor/pkg/sentry/memmap"
23+
"gvisor.dev/gvisor/pkg/syserror"
24+
"gvisor.dev/gvisor/pkg/usermem"
25+
)
26+
27+
// opathFD implements vfs.FileDescriptionImpl for a file description opened with O_PATH.
28+
//
29+
// +stateify savable
30+
type opathFD struct {
31+
vfsfd FileDescription
32+
FileDescriptionDefaultImpl
33+
NoLockFD
34+
}
35+
36+
// Release implements vfs.FileDescriptionImpl.Release.
37+
func (fd *opathFD) Release(context.Context) {
38+
// noop
39+
}
40+
41+
// Allocate implements vfs.FileDescriptionImpl.Allocate.
42+
func (fd *opathFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
43+
return syserror.EBADF
44+
}
45+
46+
// PRead implements vfs.FileDescriptionImpl.PRead.
47+
func (fd *opathFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
48+
return 0, syserror.EBADF
49+
}
50+
51+
// Read implements vfs.FileDescriptionImpl.Read.
52+
func (fd *opathFD) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
53+
return 0, syserror.EBADF
54+
}
55+
56+
// PWrite implements vfs.FileDescriptionImpl.PWrite.
57+
func (fd *opathFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
58+
return 0, syserror.EBADF
59+
}
60+
61+
// Write implements vfs.FileDescriptionImpl.Write.
62+
func (fd *opathFD) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
63+
return 0, syserror.EBADF
64+
}
65+
66+
// Ioctl implements vfs.FileDescriptionImpl.Ioctl.
67+
func (fd *opathFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) {
68+
return 0, syserror.EBADF
69+
}
70+
71+
// IterDirents implements vfs.FileDescriptionImpl.IterDirents.
72+
func (fd *opathFD) IterDirents(ctx context.Context, cb IterDirentsCallback) error {
73+
return syserror.EBADF
74+
}
75+
76+
// Seek implements vfs.FileDescriptionImpl.Seek.
77+
func (fd *opathFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
78+
return 0, syserror.EBADF
79+
}
80+
81+
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
82+
func (fd *opathFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
83+
return syserror.EBADF
84+
}
85+
86+
// ListXattr implements vfs.FileDescriptionImpl.ListXattr.
87+
func (fd *opathFD) ListXattr(ctx context.Context, size uint64) ([]string, error) {
88+
return nil, syserror.EBADF
89+
}
90+
91+
// GetXattr implements vfs.FileDescriptionImpl.GetXattr.
92+
func (fd *opathFD) GetXattr(ctx context.Context, opts GetXattrOptions) (string, error) {
93+
return "", syserror.EBADF
94+
}
95+
96+
// SetXattr implements vfs.FileDescriptionImpl.SetXattr.
97+
func (fd *opathFD) SetXattr(ctx context.Context, opts SetXattrOptions) error {
98+
return syserror.EBADF
99+
}
100+
101+
// RemoveXattr implements vfs.FileDescriptionImpl.RemoveXattr.
102+
func (fd *opathFD) RemoveXattr(ctx context.Context, name string) error {
103+
return syserror.EBADF
104+
}
105+
106+
// Sync implements vfs.FileDescriptionImpl.Sync.
107+
func (fd *opathFD) Sync(ctx context.Context) error {
108+
return syserror.EBADF
109+
}
110+
111+
// SetStat implements vfs.FileDescriptionImpl.SetStat.
112+
func (fd *opathFD) SetStat(ctx context.Context, opts SetStatOptions) error {
113+
return syserror.EBADF
114+
}
115+
116+
// Stat implements vfs.FileDescriptionImpl.Stat.
117+
func (fd *opathFD) Stat(ctx context.Context, opts StatOptions) (linux.Statx, error) {
118+
vfsObj := fd.vfsfd.vd.mount.vfs
119+
rp := vfsObj.getResolvingPath(auth.CredentialsFromContext(ctx), &PathOperation{
120+
Root: fd.vfsfd.vd,
121+
Start: fd.vfsfd.vd,
122+
})
123+
stat, err := fd.vfsfd.vd.mount.fs.impl.StatAt(ctx, rp, opts)
124+
vfsObj.putResolvingPath(ctx, rp)
125+
return stat, err
126+
}
127+
128+
// StatFS returns metadata for the filesystem containing the file represented
129+
// by fd.
130+
func (fd *opathFD) StatFS(ctx context.Context) (linux.Statfs, error) {
131+
vfsObj := fd.vfsfd.vd.mount.vfs
132+
rp := vfsObj.getResolvingPath(auth.CredentialsFromContext(ctx), &PathOperation{
133+
Root: fd.vfsfd.vd,
134+
Start: fd.vfsfd.vd,
135+
})
136+
statfs, err := fd.vfsfd.vd.mount.fs.impl.StatFSAt(ctx, rp)
137+
vfsObj.putResolvingPath(ctx, rp)
138+
return statfs, err
139+
}

pkg/sentry/vfs/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ type OpenOptions struct {
129129
//
130130
// FilesystemImpls are responsible for implementing the following flags:
131131
// O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, O_CREAT, O_DIRECT, O_DSYNC,
132-
// O_EXCL, O_NOATIME, O_NOCTTY, O_NONBLOCK, O_PATH, O_SYNC, O_TMPFILE, and
132+
// O_EXCL, O_NOATIME, O_NOCTTY, O_NONBLOCK, O_SYNC, O_TMPFILE, and
133133
// O_TRUNC. VFS is responsible for handling O_DIRECTORY, O_LARGEFILE, and
134134
// O_NOFOLLOW. VFS users are responsible for handling O_CLOEXEC, since file
135135
// descriptors are mostly outside the scope of VFS.

pkg/sentry/vfs/vfs.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,18 @@ func (vfs *VirtualFilesystem) OpenAt(ctx context.Context, creds *auth.Credential
425425
rp.mustBeDir = true
426426
rp.mustBeDirOrig = true
427427
}
428+
if opts.Flags&linux.O_PATH != 0 {
429+
vd, err := vfs.GetDentryAt(ctx, creds, pop, &GetDentryOptions{})
430+
if err != nil {
431+
return nil, err
432+
}
433+
fd := &opathFD{}
434+
if err := fd.vfsfd.Init(fd, opts.Flags, vd.Mount(), vd.Dentry(), &FileDescriptionOptions{}); err != nil {
435+
return nil, err
436+
}
437+
vd.DecRef(ctx)
438+
return &fd.vfsfd, err
439+
}
428440
for {
429441
fd, err := rp.mount.fs.impl.OpenAt(ctx, rp, *opts)
430442
if err == nil {

test/syscalls/linux/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,7 @@ cc_binary(
582582
"//test/util:eventfd_util",
583583
"//test/util:file_descriptor",
584584
gtest,
585+
"//test/util:fs_util",
585586
"//test/util:posix_error",
586587
"//test/util:temp_path",
587588
"//test/util:test_main",

test/syscalls/linux/chmod.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,42 @@ TEST(ChmodTest, FchmodatBadF) {
9898
ASSERT_THAT(fchmodat(-1, "foo", 0444, 0), SyscallFailsWithErrno(EBADF));
9999
}
100100

101+
TEST(ChmodTest, FchmodFileWithOpath) {
102+
SKIP_IF(IsRunningWithVFS1());
103+
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
104+
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH));
105+
106+
ASSERT_THAT(fchmod(fd.get(), 0444), SyscallFailsWithErrno(EBADF));
107+
}
108+
109+
TEST(ChmodTest, FchmodDirWithOpath) {
110+
SKIP_IF(IsRunningWithVFS1());
111+
const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
112+
const auto fd =
113+
ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_PATH));
114+
115+
ASSERT_THAT(fchmod(fd.get(), 0444), SyscallFailsWithErrno(EBADF));
116+
}
117+
118+
TEST(ChmodTest, FchmodatWithOpath) {
119+
SKIP_IF(IsRunningWithVFS1());
120+
// Drop capabilities that allow us to override file permissions.
121+
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
122+
123+
auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
124+
125+
const auto parent_fd = ASSERT_NO_ERRNO_AND_VALUE(
126+
Open(GetAbsoluteTestTmpdir().c_str(), O_PATH | O_DIRECTORY));
127+
128+
ASSERT_THAT(
129+
fchmodat(parent_fd.get(), std::string(Basename(temp_file.path())).c_str(),
130+
0444, 0),
131+
SyscallSucceeds());
132+
133+
EXPECT_THAT(open(temp_file.path().c_str(), O_RDWR),
134+
SyscallFailsWithErrno(EACCES));
135+
}
136+
101137
TEST(ChmodTest, FchmodatNotDir) {
102138
ASSERT_THAT(fchmodat(-1, "", 0444, 0), SyscallFailsWithErrno(ENOENT));
103139
}

test/syscalls/linux/chown.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,36 @@ TEST(ChownTest, FchownatBadF) {
4848
ASSERT_THAT(fchownat(-1, "fff", 0, 0, 0), SyscallFailsWithErrno(EBADF));
4949
}
5050

51+
TEST(ChownTest, FchownFileWithOpath) {
52+
SKIP_IF(IsRunningWithVFS1());
53+
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
54+
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH));
55+
56+
ASSERT_THAT(fchown(fd.get(), geteuid(), getegid()),
57+
SyscallFailsWithErrno(EBADF));
58+
}
59+
60+
TEST(ChownTest, FchownDirWithOpath) {
61+
SKIP_IF(IsRunningWithVFS1());
62+
const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
63+
const auto fd =
64+
ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_PATH));
65+
66+
ASSERT_THAT(fchown(fd.get(), geteuid(), getegid()),
67+
SyscallFailsWithErrno(EBADF));
68+
}
69+
70+
TEST(ChownTest, FchownatWithOpath) {
71+
SKIP_IF(IsRunningWithVFS1());
72+
const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
73+
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path()));
74+
const auto dirfd =
75+
ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_PATH));
76+
ASSERT_THAT(
77+
fchownat(dirfd.get(), file.path().c_str(), geteuid(), getegid(), 0),
78+
SyscallSucceeds());
79+
}
80+
5181
TEST(ChownTest, FchownatEmptyPath) {
5282
const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
5383
const auto fd =
@@ -209,6 +239,14 @@ INSTANTIATE_TEST_SUITE_P(
209239
owner, group, 0);
210240
MaybeSave();
211241
return errorFromReturn("fchownat-dirfd", rc);
242+
},
243+
[](const std::string& path, uid_t owner, gid_t group) -> PosixError {
244+
ASSIGN_OR_RETURN_ERRNO(auto dirfd, Open(std::string(Dirname(path)),
245+
O_DIRECTORY | O_PATH));
246+
int rc = fchownat(dirfd.get(), std::string(Basename(path)).c_str(),
247+
owner, group, 0);
248+
MaybeSave();
249+
return errorFromReturn("fchownat-opathdirfd", rc);
212250
}));
213251

214252
} // namespace

0 commit comments

Comments
 (0)