Add reference count checking to the fsimpl/host package.

Includes a minor refactor for inode construction.

Updates #1486.

PiperOrigin-RevId: 327694933
This commit is contained in:
Dean Deng
2020-08-20 14:12:52 -07:00
committed by gVisor bot
parent 129018ab3d
commit 3163aff866
3 changed files with 88 additions and 51 deletions
+25
View File
@@ -1,12 +1,37 @@
load("//tools:defs.bzl", "go_library")
load("//tools/go_generics:defs.bzl", "go_template_instance")
licenses(["notice"])
go_template_instance(
name = "inode_refs",
out = "inode_refs.go",
package = "host",
prefix = "inode",
template = "//pkg/refs_vfs2:refs_template",
types = {
"T": "inode",
},
)
go_template_instance(
name = "connected_endpoint_refs",
out = "connected_endpoint_refs.go",
package = "host",
prefix = "ConnectedEndpoint",
template = "//pkg/refs_vfs2:refs_template",
types = {
"T": "ConnectedEndpoint",
},
)
go_library(
name = "host",
srcs = [
"connected_endpoint_refs.go",
"control.go",
"host.go",
"inode_refs.go",
"ioctl_unsafe.go",
"mmap.go",
"socket.go",
+54 -40
View File
@@ -27,7 +27,6 @@ import (
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/refs"
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/hostfd"
@@ -41,6 +40,44 @@ import (
"gvisor.dev/gvisor/pkg/waiter"
)
func newInode(fs *filesystem, hostFD int, fileType linux.FileMode, isTTY bool) (*inode, error) {
// Determine if hostFD is seekable. If not, this syscall will return ESPIPE
// (see fs/read_write.c:llseek), e.g. for pipes, sockets, and some character
// devices.
_, err := unix.Seek(hostFD, 0, linux.SEEK_CUR)
seekable := err != syserror.ESPIPE
i := &inode{
hostFD: hostFD,
ino: fs.NextIno(),
isTTY: isTTY,
wouldBlock: wouldBlock(uint32(fileType)),
seekable: seekable,
// NOTE(b/38213152): Technically, some obscure char devices can be memory
// mapped, but we only allow regular files.
canMap: fileType == linux.S_IFREG,
}
i.pf.inode = i
i.refs.EnableLeakCheck()
// Non-seekable files can't be memory mapped, assert this.
if !i.seekable && i.canMap {
panic("files that can return EWOULDBLOCK (sockets, pipes, etc.) cannot be memory mapped")
}
// If the hostFD would block, we must set it to non-blocking and handle
// blocking behavior in the sentry.
if i.wouldBlock {
if err := syscall.SetNonblock(i.hostFD, true); err != nil {
return nil, err
}
if err := fdnotifier.AddFD(int32(i.hostFD), &i.queue); err != nil {
return nil, err
}
}
return i, nil
}
// NewFDOptions contains options to NewFD.
type NewFDOptions struct {
// If IsTTY is true, the file descriptor is a TTY.
@@ -76,44 +113,11 @@ func NewFD(ctx context.Context, mnt *vfs.Mount, hostFD int, opts *NewFDOptions)
flags = uint32(flagsInt)
}
fileMode := linux.FileMode(s.Mode)
fileType := fileMode.FileType()
// Determine if hostFD is seekable. If not, this syscall will return ESPIPE
// (see fs/read_write.c:llseek), e.g. for pipes, sockets, and some character
// devices.
_, err := unix.Seek(hostFD, 0, linux.SEEK_CUR)
seekable := err != syserror.ESPIPE
i := &inode{
hostFD: hostFD,
ino: fs.NextIno(),
isTTY: opts.IsTTY,
wouldBlock: wouldBlock(uint32(fileType)),
seekable: seekable,
// NOTE(b/38213152): Technically, some obscure char devices can be memory
// mapped, but we only allow regular files.
canMap: fileType == linux.S_IFREG,
}
i.pf.inode = i
// Non-seekable files can't be memory mapped, assert this.
if !i.seekable && i.canMap {
panic("files that can return EWOULDBLOCK (sockets, pipes, etc.) cannot be memory mapped")
}
// If the hostFD would block, we must set it to non-blocking and handle
// blocking behavior in the sentry.
if i.wouldBlock {
if err := syscall.SetNonblock(i.hostFD, true); err != nil {
return nil, err
}
if err := fdnotifier.AddFD(int32(i.hostFD), &i.queue); err != nil {
return nil, err
}
}
d := &kernfs.Dentry{}
i, err := newInode(fs, hostFD, linux.FileMode(s.Mode).FileType(), opts.IsTTY)
if err != nil {
return nil, err
}
d.Init(i)
// i.open will take a reference on d.
@@ -188,7 +192,7 @@ type inode struct {
locks vfs.FileLocks
// When the reference count reaches zero, the host fd is closed.
refs.AtomicRefCount
refs inodeRefs
// hostFD contains the host fd that this file was originally created from,
// which must be available at time of restore.
@@ -430,9 +434,19 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
return nil
}
// IncRef implements kernfs.Inode.
func (i *inode) IncRef() {
i.refs.IncRef()
}
// TryIncRef implements kernfs.Inode.
func (i *inode) TryIncRef() bool {
return i.refs.TryIncRef()
}
// DecRef implements kernfs.Inode.
func (i *inode) DecRef(ctx context.Context) {
i.AtomicRefCount.DecRefWithDestructor(ctx, func(context.Context) {
i.refs.DecRef(func() {
if i.wouldBlock {
fdnotifier.RemoveFD(int32(i.hostFD))
}
+9 -11
View File
@@ -22,7 +22,6 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/socket/control"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
@@ -59,8 +58,7 @@ func newEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue) (transpor
//
// +stateify savable
type ConnectedEndpoint struct {
// ref keeps track of references to a ConnectedEndpoint.
ref refs.AtomicRefCount
ConnectedEndpointRefs
// mu protects fd below.
mu sync.RWMutex `state:"nosave"`
@@ -132,9 +130,9 @@ func NewConnectedEndpoint(ctx context.Context, hostFD int, addr string, saveable
return nil, err
}
// AtomicRefCounters start off with a single reference. We need two.
e.ref.IncRef()
e.ref.EnableLeakCheck("host.ConnectedEndpoint")
// ConnectedEndpointRefs start off with a single reference. We need two.
e.IncRef()
e.EnableLeakCheck()
return &e, nil
}
@@ -318,7 +316,7 @@ func (c *ConnectedEndpoint) destroyLocked() {
// Release implements transport.ConnectedEndpoint.Release and
// transport.Receiver.Release.
func (c *ConnectedEndpoint) Release(ctx context.Context) {
c.ref.DecRefWithDestructor(ctx, func(context.Context) {
c.DecRef(func() {
c.mu.Lock()
c.destroyLocked()
c.mu.Unlock()
@@ -348,7 +346,7 @@ func (e *SCMConnectedEndpoint) Init() error {
// Release implements transport.ConnectedEndpoint.Release and
// transport.Receiver.Release.
func (e *SCMConnectedEndpoint) Release(ctx context.Context) {
e.ref.DecRefWithDestructor(ctx, func(context.Context) {
e.DecRef(func() {
e.mu.Lock()
if err := syscall.Close(e.fd); err != nil {
log.Warningf("Failed to close host fd %d: %v", err)
@@ -378,8 +376,8 @@ func NewSCMEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue, addr s
return nil, err
}
// AtomicRefCounters start off with a single reference. We need two.
e.ref.IncRef()
e.ref.EnableLeakCheck("host.SCMConnectedEndpoint")
// ConnectedEndpointRefs start off with a single reference. We need two.
e.IncRef()
e.EnableLeakCheck()
return &e, nil
}