Fix mount refcount issue.

Each mount is holds a reference on a root Dirent, but the mount itself may
live beyond it's own reference. This means that a call to Root() can come
after the associated reference has been dropped.

Instead of introducing a separate layer of references for mount objects,
we simply change the Root() method to use TryIncRef() and allow it to return
nil if the mount is already gone. This requires updating a small number of
callers and minimizes the change (since VFSv2 will replace this code shortly).

PiperOrigin-RevId: 297174230
This commit is contained in:
Adin Scannell
2020-02-25 12:17:52 -08:00
committed by gVisor bot
parent d7b7379251
commit 53504e29ca
3 changed files with 25 additions and 12 deletions
+6 -5
View File
@@ -36,11 +36,12 @@ func mountPathsAre(root *Dirent, got []*Mount, want ...string) error {
gotPaths := make(map[string]struct{}, len(got))
gotStr := make([]string, len(got))
for i, g := range got {
groot := g.Root()
name, _ := groot.FullName(root)
groot.DecRef()
gotStr[i] = name
gotPaths[name] = struct{}{}
if groot := g.Root(); groot != nil {
name, _ := groot.FullName(root)
groot.DecRef()
gotStr[i] = name
gotPaths[name] = struct{}{}
}
}
if len(got) != len(want) {
return fmt.Errorf("mount paths are different, got: %q, want: %q", gotStr, want)
+7 -3
View File
@@ -100,10 +100,14 @@ func newUndoMount(d *Dirent) *Mount {
}
}
// Root returns the root dirent of this mount. Callers must call DecRef on the
// returned dirent.
// Root returns the root dirent of this mount.
//
// This may return nil if the mount has already been free. Callers must handle this
// case appropriately. If non-nil, callers must call DecRef on the returned *Dirent.
func (m *Mount) Root() *Dirent {
m.root.IncRef()
if !m.root.TryIncRef() {
return nil
}
return m.root
}
+12 -4
View File
@@ -60,13 +60,15 @@ func forEachMount(t *kernel.Task, fn func(string, *fs.Mount)) {
})
for _, m := range ms {
mroot := m.Root()
if mroot == nil {
continue // No longer valid.
}
mountPath, desc := mroot.FullName(rootDir)
mroot.DecRef()
if !desc {
// MountSources that are not descendants of the chroot jail are ignored.
continue
}
fn(mountPath, m)
}
}
@@ -91,6 +93,12 @@ func (mif *mountInfoFile) ReadSeqFileData(ctx context.Context, handle seqfile.Se
var buf bytes.Buffer
forEachMount(mif.t, func(mountPath string, m *fs.Mount) {
mroot := m.Root()
if mroot == nil {
return // No longer valid.
}
defer mroot.DecRef()
// Format:
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
// (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
@@ -107,9 +115,6 @@ func (mif *mountInfoFile) ReadSeqFileData(ctx context.Context, handle seqfile.Se
// (3) Major:Minor device ID. We don't have a superblock, so we
// just use the root inode device number.
mroot := m.Root()
defer mroot.DecRef()
sa := mroot.Inode.StableAttr
fmt.Fprintf(&buf, "%d:%d ", sa.DeviceFileMajor, sa.DeviceFileMinor)
@@ -207,6 +212,9 @@ func (mf *mountsFile) ReadSeqFileData(ctx context.Context, handle seqfile.SeqHan
//
// The "needs dump"and fsck flags are always 0, which is allowed.
root := m.Root()
if root == nil {
return // No longer valid.
}
defer root.DecRef()
flags := root.Inode.MountSource.Flags