mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user