mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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",
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user