mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Fix sentry panic during path resolution from unlinked directories in tmpfs.
In tmpfs, child directories did not hold a reference to their parents. If a directory was unlinked but kept alive, for example, cwd, its parent could also be unlinked and have its refcount drop to 0 and destroyed. When the process in the child directory subsequently tried to `cd ..`, it resolved to the parent dir and called `IncRef` on it. Since the parent directory was already destroyed, this triggered the "non-positive count" panic. Assisted-by: jetski PiperOrigin-RevId: 922522308
This commit is contained in:
@@ -63,7 +63,11 @@ func (fs *filesystem) newDirectory(kuid auth.KUID, kgid auth.KGID, mode linux.Fi
|
||||
// - filesystem.mu must be locked for writing.
|
||||
// - dir must not already contain a child with the given name.
|
||||
func (dir *directory) insertChildLocked(child *dentry, name string) {
|
||||
isNew := child.parent.Load() == nil
|
||||
genericSetParentAndName(dir.dentry.inode.fs, child, &dir.dentry, name)
|
||||
if child.inode.isDir() && isNew {
|
||||
dir.inode.incRef()
|
||||
}
|
||||
if dir.childMap == nil {
|
||||
dir.childMap = make(map[string]*dentry)
|
||||
}
|
||||
|
||||
@@ -683,6 +683,8 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
|
||||
if renamed.inode.isDir() {
|
||||
oldParentDir.inode.decLinksLocked(ctx)
|
||||
newParentDir.inode.incLinksLocked()
|
||||
newParentDir.inode.incRef()
|
||||
oldParentDir.inode.decRef(ctx)
|
||||
}
|
||||
newParentDir.inode.touchCMtime()
|
||||
}
|
||||
|
||||
@@ -707,6 +707,10 @@ func (i *inode) decRef(ctx context.Context) {
|
||||
i.watches.HandleDeletion(ctx)
|
||||
// Remove pages used if child being removed is a SymLink or Regular File.
|
||||
switch impl := i.impl.(type) {
|
||||
case *directory:
|
||||
if parent := impl.dentry.parent.Load(); parent != nil {
|
||||
parent.inode.decRef(ctx)
|
||||
}
|
||||
case *symlink:
|
||||
if len(impl.target) >= shortSymlinkLen {
|
||||
impl.inode.fs.unaccountPages(1)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/atomicbitops"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/contexttest"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
@@ -196,3 +197,83 @@ func TestParseSize(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRmdirParentOfWD(t *testing.T) {
|
||||
ctx := contexttest.Context(t)
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
|
||||
vfsObj := &vfs.VirtualFilesystem{}
|
||||
if err := vfsObj.Init(ctx); err != nil {
|
||||
t.Fatalf("VFS init: %v", err)
|
||||
}
|
||||
|
||||
vfsObj.MustRegisterFilesystemType("tmpfs", FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
|
||||
AllowUserMount: true,
|
||||
})
|
||||
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmpfs root mount: %v", err)
|
||||
}
|
||||
defer mntns.DecRef(ctx)
|
||||
|
||||
root := mntns.Root(ctx)
|
||||
defer root.DecRef(ctx)
|
||||
|
||||
ctx = vfs.WithMountNamespace(ctx, mntns)
|
||||
|
||||
if err := vfsObj.MkdirAt(ctx, creds, &vfs.PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse("parent"),
|
||||
}, &vfs.MkdirOptions{
|
||||
Mode: 0755,
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to create parent dir: %v", err)
|
||||
}
|
||||
|
||||
if err := vfsObj.MkdirAt(ctx, creds, &vfs.PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse("parent/child"),
|
||||
}, &vfs.MkdirOptions{
|
||||
Mode: 0755,
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to create child dir: %v", err)
|
||||
}
|
||||
|
||||
childVD, err := vfsObj.GetDentryAt(ctx, creds, &vfs.PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse("parent/child"),
|
||||
}, &vfs.GetDentryOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open child dir: %v", err)
|
||||
}
|
||||
defer childVD.DecRef(ctx)
|
||||
|
||||
if err := vfsObj.RmdirAt(ctx, creds, &vfs.PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse("parent/child"),
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to rmdir child: %v", err)
|
||||
}
|
||||
|
||||
if err := vfsObj.RmdirAt(ctx, creds, &vfs.PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse("parent"),
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to rmdir parent: %v", err)
|
||||
}
|
||||
|
||||
parentVD, err := vfsObj.GetDentryAt(ctx, creds, &vfs.PathOperation{
|
||||
Root: root,
|
||||
Start: childVD,
|
||||
Path: fspath.Parse(".."),
|
||||
}, &vfs.GetDentryOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("GetDentryAt(..) failed: %v", err)
|
||||
}
|
||||
parentVD.DecRef(ctx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user