Remove p9.fidRef.openedMu

openedMu has lock ordering violations. Most locks go through OpenedFlag(),
which is usually taken after renameMu and opMu. On the other hand, Tlopen takes
openedMu before renameMu and opMu (via safelyRead).

Resolving this violation is simple: just drop openedMu. The opened and
openFlags fields are already protected by opMu in most cases, renameMu (for
write) in one case (via safelyGlobal), and only in doWalk by neither.

This is a bit ugly because opMu is supposed to be a "semantic" lock, but it
works. I'm open to other suggestions.

Note that doWalk has a race condition where a FID may open after the open check
but before actually walking. This race existed before this change as well; it
is not clear if it is problematic.

PiperOrigin-RevId: 346108483
This commit is contained in:
Michael Pratt 2020-12-07 09:21:33 -08:00 committed by gVisor bot
parent eeb23531eb
commit b28dc25aea
2 changed files with 44 additions and 51 deletions

View File

@ -296,25 +296,6 @@ func (t *Tlopen) handle(cs *connState) message {
}
defer ref.DecRef()
ref.openedMu.Lock()
defer ref.openedMu.Unlock()
// Has it been opened already?
if ref.opened || !CanOpen(ref.mode) {
return newErr(syscall.EINVAL)
}
if ref.mode.IsDir() {
// Directory must be opened ReadOnly.
if t.Flags&OpenFlagsModeMask != ReadOnly {
return newErr(syscall.EISDIR)
}
// Directory not truncatable.
if t.Flags&OpenTruncate != 0 {
return newErr(syscall.EISDIR)
}
}
var (
qid QID
ioUnit uint32
@ -326,6 +307,22 @@ func (t *Tlopen) handle(cs *connState) message {
return syscall.EINVAL
}
// Has it been opened already?
if ref.opened || !CanOpen(ref.mode) {
return syscall.EINVAL
}
if ref.mode.IsDir() {
// Directory must be opened ReadOnly.
if t.Flags&OpenFlagsModeMask != ReadOnly {
return syscall.EISDIR
}
// Directory not truncatable.
if t.Flags&OpenTruncate != 0 {
return syscall.EISDIR
}
}
osFile, qid, ioUnit, err = ref.file.Open(t.Flags)
return err
}); err != nil {
@ -366,7 +363,7 @@ func (t *Tlcreate) do(cs *connState, uid UID) (*Rlcreate, error) {
}
// Not allowed on open directories.
if _, opened := ref.OpenFlags(); opened {
if ref.opened {
return syscall.EINVAL
}
@ -437,7 +434,7 @@ func (t *Tsymlink) do(cs *connState, uid UID) (*Rsymlink, error) {
}
// Not allowed on open directories.
if _, opened := ref.OpenFlags(); opened {
if ref.opened {
return syscall.EINVAL
}
@ -476,7 +473,7 @@ func (t *Tlink) handle(cs *connState) message {
}
// Not allowed on open directories.
if _, opened := ref.OpenFlags(); opened {
if ref.opened {
return syscall.EINVAL
}
@ -518,7 +515,7 @@ func (t *Trenameat) handle(cs *connState) message {
}
// Not allowed on open directories.
if _, opened := ref.OpenFlags(); opened {
if ref.opened {
return syscall.EINVAL
}
@ -561,7 +558,7 @@ func (t *Tunlinkat) handle(cs *connState) message {
}
// Not allowed on open directories.
if _, opened := ref.OpenFlags(); opened {
if ref.opened {
return syscall.EINVAL
}
@ -701,13 +698,12 @@ func (t *Tread) handle(cs *connState) message {
)
if err := ref.safelyRead(func() (err error) {
// Has it been opened already?
openFlags, opened := ref.OpenFlags()
if !opened {
if !ref.opened {
return syscall.EINVAL
}
// Can it be read? Check permissions.
if openFlags&OpenFlagsModeMask == WriteOnly {
if ref.openFlags&OpenFlagsModeMask == WriteOnly {
return syscall.EPERM
}
@ -731,13 +727,12 @@ func (t *Twrite) handle(cs *connState) message {
var n int
if err := ref.safelyRead(func() (err error) {
// Has it been opened already?
openFlags, opened := ref.OpenFlags()
if !opened {
if !ref.opened {
return syscall.EINVAL
}
// Can it be written? Check permissions.
if openFlags&OpenFlagsModeMask == ReadOnly {
if ref.openFlags&OpenFlagsModeMask == ReadOnly {
return syscall.EPERM
}
@ -778,7 +773,7 @@ func (t *Tmknod) do(cs *connState, uid UID) (*Rmknod, error) {
}
// Not allowed on open directories.
if _, opened := ref.OpenFlags(); opened {
if ref.opened {
return syscall.EINVAL
}
@ -820,7 +815,7 @@ func (t *Tmkdir) do(cs *connState, uid UID) (*Rmkdir, error) {
}
// Not allowed on open directories.
if _, opened := ref.OpenFlags(); opened {
if ref.opened {
return syscall.EINVAL
}
@ -898,13 +893,12 @@ func (t *Tallocate) handle(cs *connState) message {
if err := ref.safelyWrite(func() error {
// Has it been opened already?
openFlags, opened := ref.OpenFlags()
if !opened {
if !ref.opened {
return syscall.EINVAL
}
// Can it be written? Check permissions.
if openFlags&OpenFlagsModeMask == ReadOnly {
if ref.openFlags&OpenFlagsModeMask == ReadOnly {
return syscall.EBADF
}
@ -1049,8 +1043,8 @@ func (t *Treaddir) handle(cs *connState) message {
return syscall.EINVAL
}
// Has it been opened already?
if _, opened := ref.OpenFlags(); !opened {
// Has it been opened yet?
if !ref.opened {
return syscall.EINVAL
}
@ -1076,8 +1070,8 @@ func (t *Tfsync) handle(cs *connState) message {
defer ref.DecRef()
if err := ref.safelyRead(func() (err error) {
// Has it been opened already?
if _, opened := ref.OpenFlags(); !opened {
// Has it been opened yet?
if !ref.opened {
return syscall.EINVAL
}
@ -1185,8 +1179,13 @@ func doWalk(cs *connState, ref *fidRef, names []string, getattr bool) (qids []QI
}
// Has it been opened already?
if _, opened := ref.OpenFlags(); opened {
err = syscall.EBUSY
err = ref.safelyRead(func() (err error) {
if ref.opened {
return syscall.EBUSY
}
return nil
})
if err != nil {
return
}

View File

@ -134,12 +134,11 @@ type fidRef struct {
// The node above will be closed only when refs reaches zero.
refs int64
// openedMu protects opened and openFlags.
openedMu sync.Mutex
// opened indicates whether this has been opened already.
//
// This is updated in handlers.go.
//
// opened is protected by pathNode.opMu or renameMu (for write).
opened bool
// mode is the fidRef's mode from the walk. Only the type bits are
@ -151,6 +150,8 @@ type fidRef struct {
// openFlags is the mode used in the open.
//
// This is updated in handlers.go.
//
// openFlags is protected by pathNode.opMu or renameMu (for write).
openFlags OpenFlags
// pathNode is the current pathNode for this FID.
@ -177,13 +178,6 @@ type fidRef struct {
deleted uint32
}
// OpenFlags returns the flags the file was opened with and true iff the fid was opened previously.
func (f *fidRef) OpenFlags() (OpenFlags, bool) {
f.openedMu.Lock()
defer f.openedMu.Unlock()
return f.openFlags, f.opened
}
// IncRef increases the references on a fid.
func (f *fidRef) IncRef() {
atomic.AddInt64(&f.refs, 1)