Obtain ref on root dentry in mqfs.GetFilesystem.

As documented in FilesystemType.GetFilesystem, a reference should be taken on
the returned dentry and filesystem by GetFilesystem implementation. mqfs did
not do that.

Additionally cleanup and clarify ref counting of dentry, filesystem and mount
in mqfs.

Reported-by: syzbot+a2c54bfb6e1525228e5f@syzkaller.appspotmail.com
Reported-by: syzbot+ccd305cdab11cfebbfff@syzkaller.appspotmail.com
PiperOrigin-RevId: 405700565
This commit is contained in:
Ayush Ranjan
2021-10-26 11:59:21 -07:00
committed by gVisor bot
parent 8b2e8caad4
commit 763d7e6e39
6 changed files with 27 additions and 35 deletions
+1 -4
View File
@@ -75,6 +75,7 @@ func (ft FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualF
impl.fs.MaxCachedDentries = maxCachedDentries
impl.fs.VFSFilesystem().IncRef()
impl.root.IncRef()
return impl.fs.VFSFilesystem(), impl.root.VFSDentry(), nil
}
@@ -100,10 +101,6 @@ func maxCachedDentries(ctx context.Context, mopts map[string]string) (_ uint64,
type filesystem struct {
kernfs.Filesystem
devMinor uint32
// root is the filesystem's root dentry. Since we take a reference on it in
// GetFilesystem, we should release it when the fs is released.
root *kernfs.Dentry
}
// Release implements vfs.FilesystemImpl.Release.
+8 -15
View File
@@ -57,16 +57,19 @@ func NewRegistryImpl(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *
return nil, err
}
var dentry kernfs.Dentry
fs := &filesystem{
devMinor: devMinor,
root: &dentry,
}
fs.VFSFilesystem().Init(vfsObj, &FilesystemType{}, fs)
vfsfs := fs.VFSFilesystem()
// NewDisconnectedMount will obtain a ref on dentry and vfsfs which is
// transferred to mount. vfsfs was initiated with 1 ref already. So get rid
// of the extra ref.
defer vfsfs.DecRef(ctx)
// dentry is initialized with 1 ref which is transferred to fs.
var dentry kernfs.Dentry
dentry.InitRoot(&fs.Filesystem, fs.newRootInode(ctx, creds))
defer vfsfs.DecRef(ctx) // NewDisconnectedMount will obtain a ref on success.
mount, err := vfsObj.NewDisconnectedMount(vfsfs, dentry.VFSDentry(), &vfs.MountOptions{})
if err != nil {
@@ -82,7 +85,7 @@ func NewRegistryImpl(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *
// Get implements mq.RegistryImpl.Get.
func (r *RegistryImpl) Get(ctx context.Context, name string, access mq.AccessType, block bool, flags uint32) (*vfs.FileDescription, bool, error) {
inode, err := r.lookup(ctx, name)
inode, err := r.root.Inode().(*rootInode).Lookup(ctx, name)
if err != nil {
return nil, false, nil
}
@@ -120,7 +123,7 @@ func (r *RegistryImpl) Unlink(ctx context.Context, name string) error {
}
root := r.root.Inode().(*rootInode)
inode, err := r.lookup(ctx, name)
inode, err := root.Lookup(ctx, name)
if err != nil {
return err
}
@@ -133,16 +136,6 @@ func (r *RegistryImpl) Destroy(ctx context.Context) {
r.mount.DecRef(ctx)
}
// lookup retreives a kernfs.Inode using a name.
func (r *RegistryImpl) lookup(ctx context.Context, name string) (kernfs.Inode, error) {
inode := r.root.Inode().(*rootInode)
lookup, err := inode.Lookup(ctx, name)
if err != nil {
return nil, err
}
return lookup, nil
}
// newFD returns a new file description created using the given queue and inode.
func (r *RegistryImpl) newFD(q *mq.Queue, inode *queueInode, access mq.AccessType, block bool, flags uint32) (*vfs.FileDescription, error) {
view, err := mq.NewView(q, access, block)
+1 -8
View File
@@ -34,7 +34,6 @@ type rootInode struct {
kernfs.InodeNotSymlink
kernfs.InodeTemporary
kernfs.OrderedChildren
implStatFS
locks vfs.FileLocks
}
@@ -77,13 +76,7 @@ func (*rootInode) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, v
return linuxerr.EPERM
}
// implStatFS provides an implementation of kernfs.Inode.StatFS for message
// queues to be embedded in inodes.
//
// +stateify savable
type implStatFS struct{}
// StatFS implements kernfs.Inode.StatFS.
func (*implStatFS) StatFS(context.Context, *vfs.Filesystem) (linux.Statfs, error) {
func (*rootInode) StatFS(context.Context, *vfs.Filesystem) (linux.Statfs, error) {
return vfs.GenericStatFS(linux.MQUEUE_MAGIC), nil
}
+1 -1
View File
@@ -28,7 +28,7 @@ import (
type FilesystemType interface {
// GetFilesystem returns a Filesystem configured by the given options,
// along with its mount root. A reference is taken on the returned
// Filesystem and Dentry.
// Filesystem and Dentry whose ownership is transferred to the caller.
GetFilesystem(ctx context.Context, vfsObj *VirtualFilesystem, creds *auth.Credentials, source string, opts GetFilesystemOptions) (*Filesystem, *Dentry, error)
// Name returns the name of this FilesystemType.
+1
View File
@@ -4209,6 +4209,7 @@ cc_binary(
linkstatic = 1,
deps = [
"//test/util:capability_util",
"//test/util:cleanup",
"//test/util:fs_util",
"//test/util:mount_util",
"//test/util:posix_error",
+15 -7
View File
@@ -22,6 +22,7 @@
#include <string>
#include "test/util/capability_util.h"
#include "test/util/cleanup.h"
#include "test/util/fs_util.h"
#include "test/util/mount_util.h"
#include "test/util/posix_error.h"
@@ -286,18 +287,25 @@ TEST(MqTest, Mount) {
TEST(MqTest, MountSeveral) {
SKIP_IF(IsRunningWithVFS1() ||
!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
constexpr int numMounts = 3;
// mountDirs should outlive mountCUs and queue so that its destructor succeeds
// in unlinking the mountpoints and does not interfere with queue destruction.
testing::TempPath mountDirs[numMounts];
testing::Cleanup mountCUs[numMounts];
PosixQueue queue = ASSERT_NO_ERRNO_AND_VALUE(
MqOpen(O_RDWR | O_CREAT | O_EXCL, 0777, nullptr));
auto const dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
// Assign the pointer so it doesn't get destroyed before the second mount is
// created.
auto mnt =
ASSERT_NO_ERRNO_AND_VALUE(Mount("none", dir1.path(), "mqueue", 0, "", 0));
for (int i = 0; i < numMounts; ++i) {
mountDirs[i] = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
mountCUs[i] = ASSERT_NO_ERRNO_AND_VALUE(
Mount("none", mountDirs[i].path(), "mqueue", 0, "", 0));
}
auto const dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
ASSERT_NO_ERRNO(Mount("none", dir2.path(), "mqueue", 0, "", 0));
// Ensure that queue is visible from all mounts.
for (int i = 0; i < numMounts; ++i) {
ASSERT_NO_ERRNO(Stat(JoinPath(mountDirs[i].path(), queue.name())));
}
}
// Test mounting mqueue and opening a queue as normal file.