proc: invalidate task inodes when tasks are destroyed

PiperOrigin-RevId: 705785809
This commit is contained in:
Andrei Vagin
2024-12-13 00:58:08 -08:00
committed by gVisor bot
parent afa323bd30
commit 9fcf0b5b53
29 changed files with 227 additions and 25 deletions
+1
View File
@@ -511,6 +511,7 @@ type dir struct {
kernfs.InodeNotSymlink
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
implStatFS
locks vfs.FileLocks
+1
View File
@@ -229,6 +229,7 @@ type rootInode struct {
kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid.
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
rootInodeRefs
locks vfs.FileLocks
+1
View File
@@ -39,6 +39,7 @@ type masterInode struct {
kernfs.InodeNotAnonymous
kernfs.InodeNotDirectory
kernfs.InodeNotSymlink
kernfs.InodeFSOwned
kernfs.InodeWatches
locks vfs.FileLocks
+1
View File
@@ -39,6 +39,7 @@ type replicaInode struct {
kernfs.InodeNotDirectory
kernfs.InodeNotSymlink
kernfs.InodeWatches
kernfs.InodeFSOwned
locks vfs.FileLocks
+1
View File
@@ -50,6 +50,7 @@ type inode struct {
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.CachedMappable
kernfs.InodeFSOwned
// the owning filesystem. fs is immutable.
fs *filesystem
+1
View File
@@ -100,6 +100,7 @@ type inode struct {
kernfs.InodeNotSymlink
kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid.
kernfs.InodeWatches
kernfs.InodeFSOwned
locks vfs.FileLocks
@@ -42,6 +42,7 @@ type DynamicBytesFile struct {
InodeNotDirectory
InodeNotSymlink
InodeWatches
InodeFSOwned
locks vfs.FileLocks
// data can additionally implement vfs.WritableDynamicBytesSource to support
+8
View File
@@ -155,6 +155,14 @@ func (fs *Filesystem) invalidateRemovedChildLocked(ctx context.Context, vfsObj *
d := toInvalidate[len(toInvalidate)-1]
toInvalidate = toInvalidate[:len(toInvalidate)-1]
if d.cached {
// The dentry is removed from the cache when its
// reference counter drops to 0. It can't be removed
// from the cache here, because fs.mu isn't locked for
// write.
d.IncRef()
fs.deferDecRef(d)
}
if d.inode.Keep() {
fs.deferDecRef(d)
}
@@ -735,6 +735,7 @@ type StaticDirectory struct {
InodeWatches
OrderedChildren
StaticDirectoryRefs
InodeFSOwned
locks vfs.FileLocks
fdOpts GenericDirectoryFDOptions
@@ -845,3 +846,15 @@ type InodeNotAnonymous struct{}
func (*InodeNotAnonymous) Anonymous() bool {
return false
}
// InodeFSOwned represents inodes whose lifecycle is entirely managed by the
// filesystem.
//
// +stateify savable
type InodeFSOwned struct{}
// RegisterDentry implements Inode.RegisterDentry.
func (*InodeFSOwned) RegisterDentry(d *Dentry) {}
// UnregisterDentry implements Remove.UnregisterDentry.
func (*InodeFSOwned) UnregisterDentry(d *Dentry) {}
+30
View File
@@ -317,6 +317,27 @@ func (d *Dentry) decRefLocked(ctx context.Context) {
}
}
// Invalidate invalidates the dentry and its children.
func (d *Dentry) Invalidate(ctx context.Context) {
d.fs.mu.RLock()
defer d.fs.processDeferredDecRefs(ctx)
defer d.fs.mu.RUnlock()
parent := d.parent.Load()
if parent == nil {
return
}
parent.dirMu.Lock()
child := parent.children[d.name]
if child != d {
parent.dirMu.Unlock()
return
}
delete(parent.children, d.name)
parent.dirMu.Unlock()
d.fs.invalidateRemovedChildLocked(ctx, d.fs.vfsfs.VirtualFilesystem(), child)
}
// cacheLocked should be called after d's reference count becomes 0. The ref
// count check may happen before acquiring d.fs.mu so there might be a race
// condition where the ref count is increased again by the time the caller
@@ -451,6 +472,7 @@ func (d *Dentry) destroy(ctx context.Context) {
panic("dentry.destroy() called with references on the dentry")
}
d.inode.UnregisterDentry(d)
d.inode.DecRef(ctx) // IncRef from Init.
refs.Unregister(d)
@@ -505,6 +527,7 @@ func (d *Dentry) Init(fs *Filesystem, inode Inode) {
d.flags = atomicbitops.FromUint32(d.flags.RacyLoad() | dflagsIsSymlink)
}
refs.Register(d)
inode.RegisterDentry(d)
}
// VFSDentry returns the generic vfs dentry for this kernfs dentry.
@@ -732,6 +755,13 @@ type Inode interface {
// Anonymous indicates that the Inode is anonymous. It will never have
// a name or parent.
Anonymous() bool
// RegisterDentry is called when a new dentry representing the inode is
// created.
RegisterDentry(d *Dentry)
// UnregisterDentry is called when the specified dentry is destroyed.
UnregisterDentry(d *Dentry)
}
type inodeRefs interface {
+2
View File
@@ -109,6 +109,7 @@ type readonlyDir struct {
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
locks vfs.FileLocks
}
@@ -146,6 +147,7 @@ type dir struct {
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
locks vfs.FileLocks
+1
View File
@@ -33,6 +33,7 @@ type StaticSymlink struct {
InodeSymlink
InodeNoStatFS
InodeWatches
InodeFSOwned
target string
}
@@ -37,6 +37,7 @@ type syntheticDirectory struct {
InodeWatches
OrderedChildren
syntheticDirectoryRefs
InodeFSOwned
locks vfs.FileLocks
}
+1
View File
@@ -36,6 +36,7 @@ type rootInode struct {
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
locks vfs.FileLocks
}
+1
View File
@@ -83,6 +83,7 @@ type Inode struct {
kernfs.InodeNotDirectory
kernfs.InodeNotSymlink
kernfs.InodeWatches
kernfs.InodeFSOwned
inodeRefs
locks vfs.FileLocks
+1
View File
@@ -95,6 +95,7 @@ type inode struct {
kernfs.InodeNotSymlink
kernfs.InodeNoopRefCount
kernfs.InodeWatches
kernfs.InodeFSOwned
locks vfs.FileLocks
pipe *pipe.VFSPipe
+10
View File
@@ -1,3 +1,4 @@
load("//pkg/sync/locking:locking.bzl", "declare_rwmutex")
load("//tools:defs.bzl", "go_library", "go_test")
load("//tools/go_generics:defs.bzl", "go_template_instance")
@@ -60,9 +61,17 @@ go_template_instance(
},
)
declare_rwmutex(
name = "dentries_mutex",
out = "dentries_mutex.go",
package = "proc",
prefix = "dentries",
)
go_library(
name = "proc",
srcs = [
"dentries_mutex.go",
"fd_dir_inode_refs.go",
"fd_info_dir_inode_refs.go",
"filesystem.go",
@@ -106,6 +115,7 @@ go_library(
"//pkg/sentry/usage",
"//pkg/sentry/vfs",
"//pkg/sync",
"//pkg/sync/locking",
"//pkg/tcpip/header",
"//pkg/tcpip/network/ipv4",
"//pkg/usermem",
+1
View File
@@ -40,6 +40,7 @@ type subtasksInode struct {
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
subtasksInodeRefs
locks vfs.FileLocks
+57 -1
View File
@@ -45,6 +45,10 @@ type taskInode struct {
locks vfs.FileLocks
task *kernel.Task
dentriesMu dentriesRWMutex `state:"nosave"`
// dentries is a list of dentries to be invalidated when the task is destroyed.
dentries map[*kernfs.Dentry]struct{}
}
var _ kernfs.Inode = (*taskInode)(nil)
@@ -99,7 +103,10 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
contents["cgroup"] = fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, &taskCgroupData{task: task})
}
taskInode := &taskInode{task: task}
taskInode := &taskInode{
task: task,
dentries: make(map[*kernfs.Dentry]struct{}),
}
// Note: credentials are overridden by taskOwnedInode.
taskInode.InodeAttrs.Init(ctx, task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0555)
taskInode.InitRefs()
@@ -113,6 +120,44 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
return inode, nil
}
func (i *taskInode) TaskDestroyAction(ctx context.Context) {
i.dentriesMu.Lock()
dentries := i.dentries
i.dentries = nil
i.dentriesMu.Unlock()
for d := range dentries {
d.Invalidate(ctx)
}
}
// RegisterDentry implements kernfs.Inode.RegisterDentry.
func (i *taskInode) RegisterDentry(d *kernfs.Dentry) {
i.dentriesMu.Lock()
defer i.dentriesMu.Unlock()
if i.dentries == nil {
return
}
if len(i.dentries) == 0 && !i.task.RegisterOnDestroyAction(i) {
// The task has been destroyed.
i.dentries = nil
return
}
i.dentries[d] = struct{}{}
}
// UnregisterDentry implements kernfs.Inode.RegisterDentry.
func (i *taskInode) UnregisterDentry(d *kernfs.Dentry) {
i.dentriesMu.Lock()
defer i.dentriesMu.Unlock()
delete(i.dentries, d)
if len(i.dentries) == 0 {
i.task.UnregisterOnDestroyAction(i)
}
}
// Valid implements kernfs.Inode.Valid. This inode remains valid as long
// as the task is still running. When it's dead, another tasks with the same
// PID could replace it.
@@ -141,6 +186,17 @@ func (i *taskInode) DecRef(ctx context.Context) {
i.taskInodeRefs.DecRef(func() { i.Destroy(ctx) })
}
func (i *taskInode) Lookup(ctx context.Context, name string) (kernfs.Inode, error) {
i.dentriesMu.RLock()
if i.dentries == nil {
// The task has been destroyed and the inode invalidate callback has been executed.
i.dentriesMu.RUnlock()
return nil, linuxerr.ESRCH
}
i.dentriesMu.RUnlock()
return i.OrderedChildren.Lookup(ctx, name)
}
// taskOwnedInode implements kernfs.Inode and overrides inode owner with task
// effective user and group.
//
+3
View File
@@ -118,6 +118,7 @@ type fdDirInode struct {
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
}
var _ kernfs.Inode = (*fdDirInode)(nil)
@@ -202,6 +203,7 @@ type fdSymlink struct {
kernfs.InodeNotAnonymous
kernfs.InodeSymlink
kernfs.InodeWatches
kernfs.InodeFSOwned
fs *filesystem
task *kernel.Task
@@ -264,6 +266,7 @@ type fdInfoDirInode struct {
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
kernfs.InodeFSOwned
}
var _ kernfs.Inode = (*fdInfoDirInode)(nil)

Some files were not shown because too many files have changed in this diff Show More