Skip to content

Commit 0a99125

Browse files
avagingvisor-bot
authored andcommitted
Check MS_RDONLY for O_TRUNC before doing file truncation.
PiperOrigin-RevId: 777692904
1 parent 09c224c commit 0a99125

File tree

6 files changed

+83
-9
lines changed

6 files changed

+83
-9
lines changed

pkg/sentry/fsimpl/gofer/filesystem.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1079,8 +1079,13 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
10791079
defer d.fs.renameMu.RUnlock()
10801080
}
10811081

1082+
mnt := rp.Mount()
10821083
trunc := opts.Flags&linux.O_TRUNC != 0 && d.fileType() == linux.S_IFREG
10831084
if trunc {
1085+
if err := mnt.CheckBeginWrite(); err != nil {
1086+
return nil, err
1087+
}
1088+
defer mnt.EndWrite()
10841089
// Lock metadataMu *while* we open a regular file with O_TRUNC because
10851090
// open(2) will change the file size on server.
10861091
d.metadataMu.Lock()
@@ -1089,7 +1094,6 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
10891094

10901095
var vfd *vfs.FileDescription
10911096
var err error
1092-
mnt := rp.Mount()
10931097
switch d.fileType() {
10941098
case linux.S_IFREG:
10951099
if !d.fs.opts.regularFilesUseSpecialFileFD {

pkg/sentry/fsimpl/kernfs/filesystem.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@ func (fs *Filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
512512
// OpenAt implements vfs.FilesystemImpl.OpenAt.
513513
func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
514514
ats := vfs.AccessTypesForOpenFlags(&opts)
515+
trunc := opts.Flags&linux.O_TRUNC != 0
516+
mnt := rp.Mount()
515517

516518
// Do not create new file.
517519
if opts.Flags&linux.O_CREAT == 0 {
@@ -526,6 +528,12 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
526528
fs.mu.RUnlock()
527529
return nil, err
528530
}
531+
if trunc && d.isRegular() {
532+
if err := mnt.CheckBeginWrite(); err != nil {
533+
return nil, err
534+
}
535+
defer mnt.EndWrite()
536+
}
529537
// Open may block so we need to unlock fs.mu. IncRef d to prevent
530538
// its destruction while fs.mu is unlocked.
531539
d.IncRef()
@@ -561,6 +569,12 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
561569
if err := start.inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
562570
return nil, err
563571
}
572+
if trunc && start.isRegular() {
573+
if err := mnt.CheckBeginWrite(); err != nil {
574+
return nil, err
575+
}
576+
defer mnt.EndWrite()
577+
}
564578
// Open may block so we need to unlock fs.mu. IncRef d to prevent
565579
// its destruction while fs.mu is unlocked.
566580
start.IncRef()
@@ -612,10 +626,10 @@ afterTrailingSymlink:
612626
if err := parent.inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite); err != nil {
613627
return nil, err
614628
}
615-
if err := rp.Mount().CheckBeginWrite(); err != nil {
629+
if err := mnt.CheckBeginWrite(); err != nil {
616630
return nil, err
617631
}
618-
defer rp.Mount().EndWrite()
632+
defer mnt.EndWrite()
619633
// Create and open the child.
620634
childI, err := parent.inode.NewFile(ctx, pc, opts)
621635
if err != nil {
@@ -646,6 +660,12 @@ afterTrailingSymlink:
646660
if err := child.inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
647661
return nil, err
648662
}
663+
if trunc && child.isRegular() {
664+
if err := mnt.CheckBeginWrite(); err != nil {
665+
return nil, err
666+
}
667+
defer mnt.EndWrite()
668+
}
649669
if child.isDir() {
650670
// Can't open directories with O_CREAT.
651671
if opts.Flags&linux.O_CREAT != 0 {

pkg/sentry/fsimpl/kernfs/kernfs.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ const (
202202

203203
// Dentry points to a symlink inode.
204204
dflagsIsSymlink
205+
206+
// Dentry points to a regular file inode.
207+
dflagsIsRegular
205208
)
206209

207210
// Dentry implements vfs.DentryImpl.
@@ -519,12 +522,13 @@ func (d *Dentry) Init(fs *Filesystem, inode Inode) {
519522
d.fs = fs
520523
d.inode = inode
521524
d.refs.Store(1)
522-
ftype := inode.Mode().FileType()
523-
if ftype == linux.ModeDirectory {
525+
switch inode.Mode().FileType() {
526+
case linux.ModeDirectory:
524527
d.flags = atomicbitops.FromUint32(d.flags.RacyLoad() | dflagsIsDir)
525-
}
526-
if ftype == linux.ModeSymlink {
528+
case linux.ModeSymlink:
527529
d.flags = atomicbitops.FromUint32(d.flags.RacyLoad() | dflagsIsSymlink)
530+
case linux.ModeRegular:
531+
d.flags = atomicbitops.FromUint32(d.flags.RacyLoad() | dflagsIsRegular)
528532
}
529533
refs.Register(d)
530534
inode.RegisterDentry(d)
@@ -553,6 +557,11 @@ func (d *Dentry) isSymlink() bool {
553557
return d.flags.Load()&dflagsIsSymlink != 0
554558
}
555559

560+
// isRegular checks whether the dentry points to a regular file inode.
561+
func (d *Dentry) isRegular() bool {
562+
return d.flags.Load()&dflagsIsRegular != 0
563+
}
564+
556565
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
557566
func (d *Dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {
558567
if d.isDir() {

pkg/sentry/fsimpl/tmpfs/filesystem.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,11 +459,19 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
459459
case *regularFile:
460460
var fd regularFileFD
461461
fd.LockFD.Init(&d.inode.locks)
462-
if err := fd.vfsfd.Init(&fd, opts.Flags, rp.Mount(), &d.vfsd, &vfs.FileDescriptionOptions{AllowDirectIO: true}); err != nil {
462+
mnt := rp.Mount()
463+
if err := fd.vfsfd.Init(&fd, opts.Flags, mnt, &d.vfsd, &vfs.FileDescriptionOptions{AllowDirectIO: true}); err != nil {
463464
return nil, err
464465
}
465466
if !afterCreate && opts.Flags&linux.O_TRUNC != 0 {
466-
if _, err := impl.truncate(0); err != nil {
467+
if err := mnt.CheckBeginWrite(); err != nil {
468+
fd.vfsfd.DecRef(ctx)
469+
return nil, err
470+
}
471+
_, err := impl.truncate(0)
472+
mnt.EndWrite()
473+
if err != nil {
474+
fd.vfsfd.DecRef(ctx)
467475
return nil, err
468476
}
469477
}

test/syscalls/linux/mount.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/magic.h>
1919
#include <sched.h>
2020
#include <stdio.h>
21+
#include <string.h>
2122
#include <sys/eventfd.h>
2223
#include <sys/mman.h>
2324
#include <sys/mount.h>
@@ -446,6 +447,37 @@ TEST(MountTest, MountReadonly) {
446447
SyscallFailsWithErrno(EROFS));
447448
}
448449

450+
TEST(MountTest, BindMountReadonly) {
451+
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
452+
453+
auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
454+
auto const bindDir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
455+
auto const mnt = ASSERT_NO_ERRNO_AND_VALUE(
456+
Mount(dir.path(), bindDir.path(), "", MS_BIND, "", 0));
457+
458+
std::string const filename = JoinPath(bindDir.path(), "foo");
459+
FileDescriptor fd =
460+
ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR | O_CREAT, 0644));
461+
char msg[] = "hello world";
462+
EXPECT_THAT(pwrite64(fd.get(), msg, strlen(msg), 0),
463+
SyscallSucceedsWithValue(strlen(msg)));
464+
fd.reset();
465+
EXPECT_THAT(mount(dir.path().c_str(), bindDir.path().c_str(), NULL,
466+
MS_BIND | MS_RDONLY | MS_REMOUNT, NULL),
467+
SyscallSucceeds());
468+
469+
EXPECT_THAT(access(bindDir.path().c_str(), W_OK),
470+
SyscallFailsWithErrno(EROFS));
471+
472+
EXPECT_THAT(open(filename.c_str(), O_RDWR), SyscallFailsWithErrno(EROFS));
473+
EXPECT_THAT(open(filename.c_str(), O_RDWR | O_TRUNC),
474+
SyscallFailsWithErrno(EROFS));
475+
EXPECT_THAT(open(filename.c_str(), O_RDONLY | O_TRUNC),
476+
SyscallFailsWithErrno(EROFS));
477+
const struct stat s = ASSERT_NO_ERRNO_AND_VALUE(Stat(filename));
478+
EXPECT_EQ(s.st_size, strlen(msg));
479+
}
480+
449481
PosixErrorOr<absl::Time> ATime(absl::string_view file) {
450482
struct stat s = {};
451483
if (stat(std::string(file).c_str(), &s) == -1) {

test/syscalls/linux/open.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ TEST_F(OpenTest, CanTruncateReadOnly) {
436436
struct stat stat;
437437
EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds());
438438
EXPECT_EQ(stat.st_size, 0);
439+
EXPECT_THAT(write(fd1.get(), "x", 1), SyscallFailsWithErrno(EBADF));
439440
}
440441

441442
// If we don't have read permission on the file, opening with

0 commit comments

Comments
 (0)