Add leak checking to vfs2 structures that cannot use the refs_vfs2 template.

Updates #1486.

PiperOrigin-RevId: 338832085
This commit is contained in:
Dean Deng
2020-10-24 07:48:47 -07:00
committed by gVisor bot
parent bc814b01ab
commit 4feb5c7c26
12 changed files with 215 additions and 26 deletions
+1
View File
@@ -54,6 +54,7 @@ go_library(
"//pkg/log",
"//pkg/p9",
"//pkg/refs",
"//pkg/refsvfs2",
"//pkg/safemem",
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/fs/lock",
+4
View File
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
@@ -100,6 +101,9 @@ func (d *dentry) createSyntheticChildLocked(opts *createSyntheticOpts) {
hostFD: -1,
nlink: uint32(2),
}
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Register(child, "gofer.dentry")
}
switch opts.mode.FileType() {
case linux.S_IFDIR:
// Nothing else needs to be done.
+83 -7
View File
@@ -46,6 +46,8 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/p9"
refs_vfs1 "gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -109,8 +111,8 @@ type filesystem struct {
// cachedDentries contains all dentries with 0 references. (Due to race
// conditions, it may also contain dentries with non-zero references.)
// cachedDentriesLen is the number of dentries in cachedDentries. These
// fields are protected by renameMu.
// cachedDentriesLen is the number of dentries in cachedDentries. These fields
// are protected by renameMu.
cachedDentries dentryList
cachedDentriesLen uint64
@@ -134,6 +136,10 @@ type filesystem struct {
// savedDentryRW records open read/write handles during save/restore.
savedDentryRW map[*dentry]savedDentryRW
// released is nonzero once filesystem.Release has been called. It is accessed
// with atomic memory operations.
released int32
}
// +stateify savable
@@ -454,9 +460,8 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
return nil, nil, err
}
// Set the root's reference count to 2. One reference is returned to the
// caller, and the other is deliberately leaked to prevent the root from
// being "cached" and subsequently evicted. Its resources will still be
// cleaned up by fs.Release().
// caller, and the other is held by fs to prevent the root from being "cached"
// and subsequently evicted.
root.refs = 2
fs.root = root
@@ -526,15 +531,16 @@ func (fs *filesystem) dial(ctx context.Context) error {
// Release implements vfs.FilesystemImpl.Release.
func (fs *filesystem) Release(ctx context.Context) {
mf := fs.mfp.MemoryFile()
atomic.StoreInt32(&fs.released, 1)
mf := fs.mfp.MemoryFile()
fs.syncMu.Lock()
for d := range fs.syncableDentries {
d.handleMu.Lock()
d.dataMu.Lock()
if h := d.writeHandleLocked(); h.isOpen() {
// Write dirty cached data to the remote file.
if err := fsutil.SyncDirtyAll(ctx, &d.cache, &d.dirty, d.size, fs.mfp.MemoryFile(), h.writeFromBlocksAt); err != nil {
if err := fsutil.SyncDirtyAll(ctx, &d.cache, &d.dirty, d.size, mf, h.writeFromBlocksAt); err != nil {
log.Warningf("gofer.filesystem.Release: failed to flush dentry: %v", err)
}
// TODO(jamieliu): Do we need to flushf/fsync d?
@@ -555,6 +561,21 @@ func (fs *filesystem) Release(ctx context.Context) {
// fs.
fs.syncMu.Unlock()
// If leak checking is enabled, release all outstanding references in the
// filesystem. We deliberately avoid doing this outside of leak checking; we
// have released all external resources above rather than relying on dentry
// destructors.
if refs_vfs1.GetLeakMode() != refs_vfs1.NoLeakChecking {
fs.renameMu.Lock()
fs.root.releaseSyntheticRecursiveLocked(ctx)
fs.evictAllCachedDentriesLocked(ctx)
fs.renameMu.Unlock()
// An extra reference was held by the filesystem on the root to prevent it from
// being cached/evicted.
fs.root.DecRef(ctx)
}
if !fs.iopts.LeakConnection {
// Close the connection to the server. This implicitly clunks all fids.
fs.client.Close()
@@ -563,6 +584,31 @@ func (fs *filesystem) Release(ctx context.Context) {
fs.vfsfs.VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
}
// releaseSyntheticRecursiveLocked traverses the tree with root d and decrements
// the reference count on every synthetic dentry. Synthetic dentries have one
// reference for existence that should be dropped during filesystem.Release.
//
// Precondition: d.fs.renameMu is locked.
func (d *dentry) releaseSyntheticRecursiveLocked(ctx context.Context) {
if d.isSynthetic() {
d.decRefLocked()
d.checkCachingLocked(ctx)
}
if d.isDir() {
var children []*dentry
d.dirMu.Lock()
for _, child := range d.children {
children = append(children, child)
}
d.dirMu.Unlock()
for _, child := range children {
if child != nil {
child.releaseSyntheticRecursiveLocked(ctx)
}
}
}
}
// dentry implements vfs.DentryImpl.
//
// +stateify savable
@@ -815,6 +861,9 @@ func (fs *filesystem) newDentry(ctx context.Context, file p9file, qid p9.QID, ma
d.nlink = uint32(attr.NLink)
}
d.vfsd.Init(d)
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Register(d, "gofer.dentry")
}
fs.syncMu.Lock()
fs.syncableDentries[d] = struct{}{}
@@ -1210,6 +1259,11 @@ func (d *dentry) decRefLocked() {
}
}
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
func (d *dentry) LeakMessage() string {
return fmt.Sprintf("[gofer.dentry %p] reference count of %d instead of -1", d, atomic.LoadInt64(&d.refs))
}
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
func (d *dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {
if d.isDir() {
@@ -1292,6 +1346,16 @@ func (d *dentry) checkCachingLocked(ctx context.Context) {
if d.watches.Size() > 0 {
return
}
if atomic.LoadInt32(&d.fs.released) != 0 {
if d.parent != nil {
d.parent.dirMu.Lock()
delete(d.parent.children, d.name)
d.parent.dirMu.Unlock()
}
d.destroyLocked(ctx)
}
// If d is already cached, just move it to the front of the LRU.
if d.cached {
d.fs.cachedDentries.Remove(d)
@@ -1310,6 +1374,14 @@ func (d *dentry) checkCachingLocked(ctx context.Context) {
}
}
// Precondition: fs.renameMu must be locked for writing; it may be temporarily
// unlocked.
func (fs *filesystem) evictAllCachedDentriesLocked(ctx context.Context) {
for fs.cachedDentriesLen != 0 {
fs.evictCachedDentryLocked(ctx)
}
}
// Preconditions:
// * fs.renameMu must be locked for writing; it may be temporarily unlocked.
// * fs.cachedDentriesLen != 0.
@@ -1422,6 +1494,10 @@ func (d *dentry) destroyLocked(ctx context.Context) {
panic("gofer.dentry.DecRef() called without holding a reference")
}
}
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Unregister(d, "gofer.dentry")
}
}
func (d *dentry) isDeleted() bool {
+5 -3
View File
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
@@ -53,9 +54,7 @@ func (fs *filesystem) PrepareSave(ctx context.Context) error {
// Purge cached dentries, which may not be reopenable after restore due to
// permission changes.
fs.renameMu.Lock()
for fs.cachedDentriesLen != 0 {
fs.evictCachedDentryLocked(ctx)
}
fs.evictAllCachedDentriesLocked(ctx)
fs.renameMu.Unlock()
// Buffer pipe data so that it's available for reading after restore. (This
@@ -141,6 +140,9 @@ func (d *dentry) beforeSave() {
// afterLoad is invoked by stateify.
func (d *dentry) afterLoad() {
d.hostFD = -1
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&d.refs) != -1 {
refsvfs2.Register(d, "gofer.dentry")
}
}
// afterLoad is invoked by stateify.
+2
View File
@@ -23,6 +23,7 @@ go_library(
"fstree.go",
"overlay.go",
"regular_file.go",
"save_restore.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
@@ -30,6 +31,7 @@ go_library(
"//pkg/context",
"//pkg/fspath",
"//pkg/log",
"//pkg/refsvfs2",
"//pkg/sentry/arch",
"//pkg/sentry/fs/lock",
"//pkg/sentry/kernel/auth",
+13
View File
@@ -33,12 +33,14 @@
package overlay
import (
"fmt"
"strings"
"sync/atomic"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/refsvfs2"
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -484,6 +486,9 @@ func (fs *filesystem) newDentry() *dentry {
}
d.lowerVDs = d.inlineLowerVDs[:0]
d.vfsd.Init(d)
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Register(d, "overlay.dentry")
}
return d
}
@@ -583,6 +588,14 @@ func (d *dentry) destroyLocked(ctx context.Context) {
panic("overlay.dentry.DecRef() called without holding a reference")
}
}
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Unregister(d, "overlay.dentry")
}
}
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
func (d *dentry) LeakMessage() string {
return fmt.Sprintf("[overlay.dentry %p] reference count of %d instead of -1", d, atomic.LoadInt64(&d.refs))
}
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package overlay
import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/refsvfs2"
)
func (d *dentry) afterLoad() {
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&d.refs) != -1 {
refsvfs2.Register(d, "overlay.dentry")
}
}
+2
View File
@@ -6,6 +6,7 @@ go_library(
name = "verity",
srcs = [
"filesystem.go",
"save_restore.go",
"verity.go",
],
visibility = ["//pkg/sentry:internal"],
@@ -15,6 +16,7 @@ go_library(
"//pkg/fspath",
"//pkg/marshal/primitive",
"//pkg/merkletree",
"//pkg/refsvfs2",
"//pkg/sentry/arch",
"//pkg/sentry/fs/lock",
"//pkg/sentry/kernel",
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package verity
import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/refsvfs2"
)
func (d *dentry) afterLoad() {
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&d.refs) != -1 {
refsvfs2.Register(d, "verity.dentry")
}
}
+12
View File
@@ -31,6 +31,7 @@ import (
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/merkletree"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/sentry/arch"
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -331,6 +332,9 @@ func (fs *filesystem) newDentry() *dentry {
fs: fs,
}
d.vfsd.Init(d)
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Register(d, "verity.dentry")
}
return d
}
@@ -393,6 +397,9 @@ func (d *dentry) destroyLocked(ctx context.Context) {
if d.lowerVD.Ok() {
d.lowerVD.DecRef(ctx)
}
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Unregister(d, "verity.dentry")
}
if d.lowerMerkleVD.Ok() {
d.lowerMerkleVD.DecRef(ctx)
@@ -412,6 +419,11 @@ func (d *dentry) destroyLocked(ctx context.Context) {
}
}
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
func (d *dentry) LeakMessage() string {
return fmt.Sprintf("[verity.dentry %p] reference count of %d instead of -1", d, atomic.LoadInt64(&d.refs))
}
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
func (d *dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {
//TODO(b/159261227): Implement InotifyWithParent.
+32 -16
View File
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -106,6 +107,9 @@ func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *Mount
if opts.ReadOnly {
mnt.setReadOnlyLocked(true)
}
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Register(mnt, "vfs.Mount")
}
return mnt
}
@@ -489,26 +493,38 @@ func (mnt *Mount) IncRef() {
// DecRef decrements mnt's reference count.
func (mnt *Mount) DecRef(ctx context.Context) {
refs := atomic.AddInt64(&mnt.refs, -1)
if refs&^math.MinInt64 == 0 { // mask out MSB
var vd VirtualDentry
if mnt.parent() != nil {
mnt.vfs.mountMu.Lock()
mnt.vfs.mounts.seq.BeginWrite()
vd = mnt.vfs.disconnectLocked(mnt)
mnt.vfs.mounts.seq.EndWrite()
mnt.vfs.mountMu.Unlock()
}
if mnt.root != nil {
mnt.root.DecRef(ctx)
}
mnt.fs.DecRef(ctx)
if vd.Ok() {
vd.DecRef(ctx)
r := atomic.AddInt64(&mnt.refs, -1)
if r&^math.MinInt64 == 0 { // mask out MSB
if refsvfs2.LeakCheckEnabled() {
refsvfs2.Unregister(mnt, "vfs.Mount")
}
mnt.destroy(ctx)
}
}
func (mnt *Mount) destroy(ctx context.Context) {
var vd VirtualDentry
if mnt.parent() != nil {
mnt.vfs.mountMu.Lock()
mnt.vfs.mounts.seq.BeginWrite()
vd = mnt.vfs.disconnectLocked(mnt)
mnt.vfs.mounts.seq.EndWrite()
mnt.vfs.mountMu.Unlock()
}
if mnt.root != nil {
mnt.root.DecRef(ctx)
}
mnt.fs.DecRef(ctx)
if vd.Ok() {
vd.DecRef(ctx)
}
}
// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
func (mnt *Mount) LeakMessage() string {
return fmt.Sprintf("[vfs.Mount %p] reference count of %d instead of 0", mnt, atomic.LoadInt64(&mnt.refs))
}
// DecRef decrements mntns' reference count.
func (mntns *MountNamespace) DecRef(ctx context.Context) {
vfs := mntns.root.fs.VirtualFilesystem()
+7
View File
@@ -19,6 +19,7 @@ import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/refsvfs2"
)
// FilesystemImplSaveRestoreExtension is an optional extension to
@@ -109,6 +110,12 @@ func (vfs *VirtualFilesystem) loadMounts(mounts []*Mount) {
}
}
func (mnt *Mount) afterLoad() {
if refsvfs2.LeakCheckEnabled() && atomic.LoadInt64(&mnt.refs) != 0 {
refsvfs2.Register(mnt, "vfs.Mount")
}
}
// afterLoad is called by stateify.
func (epi *epollInterest) afterLoad() {
// Mark all epollInterests as ready after restore so that the next call to