diff options
Diffstat (limited to 'pkg/sentry/fsimpl/host')
-rw-r--r-- | pkg/sentry/fsimpl/host/BUILD | 78 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/host/connected_endpoint_refs.go | 118 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/host/host_state_autogen.go | 303 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/host/host_unsafe_state_autogen.go | 3 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/host/inode_refs.go | 118 |
5 files changed, 542 insertions, 78 deletions
diff --git a/pkg/sentry/fsimpl/host/BUILD b/pkg/sentry/fsimpl/host/BUILD deleted file mode 100644 index 56bcf9bdb..000000000 --- a/pkg/sentry/fsimpl/host/BUILD +++ /dev/null @@ -1,78 +0,0 @@ -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", - "socket_iovec.go", - "socket_unsafe.go", - "tty.go", - "util.go", - "util_unsafe.go", - ], - visibility = ["//pkg/sentry:internal"], - deps = [ - "//pkg/abi/linux", - "//pkg/context", - "//pkg/fdnotifier", - "//pkg/fspath", - "//pkg/iovec", - "//pkg/log", - "//pkg/marshal/primitive", - "//pkg/refs", - "//pkg/safemem", - "//pkg/sentry/arch", - "//pkg/sentry/fs/fsutil", - "//pkg/sentry/fs/lock", - "//pkg/sentry/fsimpl/kernfs", - "//pkg/sentry/hostfd", - "//pkg/sentry/kernel", - "//pkg/sentry/kernel/auth", - "//pkg/sentry/memmap", - "//pkg/sentry/socket/control", - "//pkg/sentry/socket/unix", - "//pkg/sentry/socket/unix/transport", - "//pkg/sentry/unimpl", - "//pkg/sentry/uniqueid", - "//pkg/sentry/vfs", - "//pkg/sync", - "//pkg/syserr", - "//pkg/syserror", - "//pkg/tcpip", - "//pkg/unet", - "//pkg/usermem", - "//pkg/waiter", - "@org_golang_x_sys//unix:go_default_library", - ], -) diff --git a/pkg/sentry/fsimpl/host/connected_endpoint_refs.go b/pkg/sentry/fsimpl/host/connected_endpoint_refs.go new file mode 100644 index 000000000..babb3f664 --- /dev/null +++ b/pkg/sentry/fsimpl/host/connected_endpoint_refs.go @@ -0,0 +1,118 @@ +package host + +import ( + "fmt" + "runtime" + "sync/atomic" + + "gvisor.dev/gvisor/pkg/log" + refs_vfs1 "gvisor.dev/gvisor/pkg/refs" +) + +// ownerType is used to customize logging. Note that we use a pointer to T so +// that we do not copy the entire object when passed as a format parameter. +var ConnectedEndpointownerType *ConnectedEndpoint + +// Refs implements refs.RefCounter. It keeps a reference count using atomic +// operations and calls the destructor when the count reaches zero. +// +// Note that the number of references is actually refCount + 1 so that a default +// zero-value Refs object contains one reference. +// +// TODO(gvisor.dev/issue/1486): Store stack traces when leak check is enabled in +// a map with 16-bit hashes, and store the hash in the top 16 bits of refCount. +// This will allow us to add stack trace information to the leak messages +// without growing the size of Refs. +// +// +stateify savable +type ConnectedEndpointRefs struct { + // refCount is composed of two fields: + // + // [32-bit speculative references]:[32-bit real references] + // + // Speculative references are used for TryIncRef, to avoid a CompareAndSwap + // loop. See IncRef, DecRef and TryIncRef for details of how these fields are + // used. + refCount int64 +} + +func (r *ConnectedEndpointRefs) finalize() { + var note string + switch refs_vfs1.GetLeakMode() { + case refs_vfs1.NoLeakChecking: + return + case refs_vfs1.UninitializedLeakChecking: + note = "(Leak checker uninitialized): " + } + if n := r.ReadRefs(); n != 0 { + log.Warningf("%sRefs %p owned by %T garbage collected with ref count of %d (want 0)", note, r, ConnectedEndpointownerType, n) + } +} + +// EnableLeakCheck checks for reference leaks when Refs gets garbage collected. +func (r *ConnectedEndpointRefs) EnableLeakCheck() { + if refs_vfs1.GetLeakMode() != refs_vfs1.NoLeakChecking { + runtime.SetFinalizer(r, (*ConnectedEndpointRefs).finalize) + } +} + +// ReadRefs returns the current number of references. The returned count is +// inherently racy and is unsafe to use without external synchronization. +func (r *ConnectedEndpointRefs) ReadRefs() int64 { + + return atomic.LoadInt64(&r.refCount) + 1 +} + +// IncRef implements refs.RefCounter.IncRef. +// +//go:nosplit +func (r *ConnectedEndpointRefs) IncRef() { + if v := atomic.AddInt64(&r.refCount, 1); v <= 0 { + panic(fmt.Sprintf("Incrementing non-positive ref count %p owned by %T", r, ConnectedEndpointownerType)) + } +} + +// TryIncRef implements refs.RefCounter.TryIncRef. +// +// To do this safely without a loop, a speculative reference is first acquired +// on the object. This allows multiple concurrent TryIncRef calls to distinguish +// other TryIncRef calls from genuine references held. +// +//go:nosplit +func (r *ConnectedEndpointRefs) TryIncRef() bool { + const speculativeRef = 1 << 32 + v := atomic.AddInt64(&r.refCount, speculativeRef) + if int32(v) < 0 { + + atomic.AddInt64(&r.refCount, -speculativeRef) + return false + } + + atomic.AddInt64(&r.refCount, -speculativeRef+1) + return true +} + +// DecRef implements refs.RefCounter.DecRef. +// +// Note that speculative references are counted here. Since they were added +// prior to real references reaching zero, they will successfully convert to +// real references. In other words, we see speculative references only in the +// following case: +// +// A: TryIncRef [speculative increase => sees non-negative references] +// B: DecRef [real decrease] +// A: TryIncRef [transform speculative to real] +// +//go:nosplit +func (r *ConnectedEndpointRefs) DecRef(destroy func()) { + switch v := atomic.AddInt64(&r.refCount, -1); { + case v < -1: + panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %T", r, ConnectedEndpointownerType)) + + case v == -1: + + if destroy != nil { + destroy() + } + } +} diff --git a/pkg/sentry/fsimpl/host/host_state_autogen.go b/pkg/sentry/fsimpl/host/host_state_autogen.go new file mode 100644 index 000000000..5aaee37c3 --- /dev/null +++ b/pkg/sentry/fsimpl/host/host_state_autogen.go @@ -0,0 +1,303 @@ +// automatically generated by stateify. + +package host + +import ( + "gvisor.dev/gvisor/pkg/state" +) + +func (r *ConnectedEndpointRefs) StateTypeName() string { + return "pkg/sentry/fsimpl/host.ConnectedEndpointRefs" +} + +func (r *ConnectedEndpointRefs) StateFields() []string { + return []string{ + "refCount", + } +} + +func (r *ConnectedEndpointRefs) beforeSave() {} + +func (r *ConnectedEndpointRefs) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.refCount) +} + +func (r *ConnectedEndpointRefs) afterLoad() {} + +func (r *ConnectedEndpointRefs) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.refCount) +} + +func (f *filesystemType) StateTypeName() string { + return "pkg/sentry/fsimpl/host.filesystemType" +} + +func (f *filesystemType) StateFields() []string { + return []string{} +} + +func (f *filesystemType) beforeSave() {} + +func (f *filesystemType) StateSave(stateSinkObject state.Sink) { + f.beforeSave() +} + +func (f *filesystemType) afterLoad() {} + +func (f *filesystemType) StateLoad(stateSourceObject state.Source) { +} + +func (fs *filesystem) StateTypeName() string { + return "pkg/sentry/fsimpl/host.filesystem" +} + +func (fs *filesystem) StateFields() []string { + return []string{ + "Filesystem", + "devMinor", + } +} + +func (fs *filesystem) beforeSave() {} + +func (fs *filesystem) StateSave(stateSinkObject state.Sink) { + fs.beforeSave() + stateSinkObject.Save(0, &fs.Filesystem) + stateSinkObject.Save(1, &fs.devMinor) +} + +func (fs *filesystem) afterLoad() {} + +func (fs *filesystem) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &fs.Filesystem) + stateSourceObject.Load(1, &fs.devMinor) +} + +func (i *inode) StateTypeName() string { + return "pkg/sentry/fsimpl/host.inode" +} + +func (i *inode) StateFields() []string { + return []string{ + "InodeNoStatFS", + "InodeNotDirectory", + "InodeNotSymlink", + "InodeTemporary", + "locks", + "inodeRefs", + "hostFD", + "ino", + "isTTY", + "seekable", + "wouldBlock", + "queue", + "canMap", + "mappings", + "pf", + } +} + +func (i *inode) beforeSave() {} + +func (i *inode) StateSave(stateSinkObject state.Sink) { + i.beforeSave() + stateSinkObject.Save(0, &i.InodeNoStatFS) + stateSinkObject.Save(1, &i.InodeNotDirectory) + stateSinkObject.Save(2, &i.InodeNotSymlink) + stateSinkObject.Save(3, &i.InodeTemporary) + stateSinkObject.Save(4, &i.locks) + stateSinkObject.Save(5, &i.inodeRefs) + stateSinkObject.Save(6, &i.hostFD) + stateSinkObject.Save(7, &i.ino) + stateSinkObject.Save(8, &i.isTTY) + stateSinkObject.Save(9, &i.seekable) + stateSinkObject.Save(10, &i.wouldBlock) + stateSinkObject.Save(11, &i.queue) + stateSinkObject.Save(12, &i.canMap) + stateSinkObject.Save(13, &i.mappings) + stateSinkObject.Save(14, &i.pf) +} + +func (i *inode) afterLoad() {} + +func (i *inode) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &i.InodeNoStatFS) + stateSourceObject.Load(1, &i.InodeNotDirectory) + stateSourceObject.Load(2, &i.InodeNotSymlink) + stateSourceObject.Load(3, &i.InodeTemporary) + stateSourceObject.Load(4, &i.locks) + stateSourceObject.Load(5, &i.inodeRefs) + stateSourceObject.Load(6, &i.hostFD) + stateSourceObject.Load(7, &i.ino) + stateSourceObject.Load(8, &i.isTTY) + stateSourceObject.Load(9, &i.seekable) + stateSourceObject.Load(10, &i.wouldBlock) + stateSourceObject.Load(11, &i.queue) + stateSourceObject.Load(12, &i.canMap) + stateSourceObject.Load(13, &i.mappings) + stateSourceObject.Load(14, &i.pf) +} + +func (f *fileDescription) StateTypeName() string { + return "pkg/sentry/fsimpl/host.fileDescription" +} + +func (f *fileDescription) StateFields() []string { + return []string{ + "vfsfd", + "FileDescriptionDefaultImpl", + "LockFD", + "inode", + "offset", + } +} + +func (f *fileDescription) beforeSave() {} + +func (f *fileDescription) StateSave(stateSinkObject state.Sink) { + f.beforeSave() + stateSinkObject.Save(0, &f.vfsfd) + stateSinkObject.Save(1, &f.FileDescriptionDefaultImpl) + stateSinkObject.Save(2, &f.LockFD) + stateSinkObject.Save(3, &f.inode) + stateSinkObject.Save(4, &f.offset) +} + +func (f *fileDescription) afterLoad() {} + +func (f *fileDescription) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &f.vfsfd) + stateSourceObject.Load(1, &f.FileDescriptionDefaultImpl) + stateSourceObject.Load(2, &f.LockFD) + stateSourceObject.Load(3, &f.inode) + stateSourceObject.Load(4, &f.offset) +} + +func (r *inodeRefs) StateTypeName() string { + return "pkg/sentry/fsimpl/host.inodeRefs" +} + +func (r *inodeRefs) StateFields() []string { + return []string{ + "refCount", + } +} + +func (r *inodeRefs) beforeSave() {} + +func (r *inodeRefs) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.refCount) +} + +func (r *inodeRefs) afterLoad() {} + +func (r *inodeRefs) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.refCount) +} + +func (i *inodePlatformFile) StateTypeName() string { + return "pkg/sentry/fsimpl/host.inodePlatformFile" +} + +func (i *inodePlatformFile) StateFields() []string { + return []string{ + "inode", + "fdRefs", + "fileMapper", + } +} + +func (i *inodePlatformFile) beforeSave() {} + +func (i *inodePlatformFile) StateSave(stateSinkObject state.Sink) { + i.beforeSave() + stateSinkObject.Save(0, &i.inode) + stateSinkObject.Save(1, &i.fdRefs) + stateSinkObject.Save(2, &i.fileMapper) +} + +func (i *inodePlatformFile) afterLoad() {} + +func (i *inodePlatformFile) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &i.inode) + stateSourceObject.Load(1, &i.fdRefs) + stateSourceObject.Load(2, &i.fileMapper) +} + +func (c *ConnectedEndpoint) StateTypeName() string { + return "pkg/sentry/fsimpl/host.ConnectedEndpoint" +} + +func (c *ConnectedEndpoint) StateFields() []string { + return []string{ + "ConnectedEndpointRefs", + "fd", + "addr", + "stype", + } +} + +func (c *ConnectedEndpoint) beforeSave() {} + +func (c *ConnectedEndpoint) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.ConnectedEndpointRefs) + stateSinkObject.Save(1, &c.fd) + stateSinkObject.Save(2, &c.addr) + stateSinkObject.Save(3, &c.stype) +} + +func (c *ConnectedEndpoint) afterLoad() {} + +func (c *ConnectedEndpoint) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.ConnectedEndpointRefs) + stateSourceObject.Load(1, &c.fd) + stateSourceObject.Load(2, &c.addr) + stateSourceObject.Load(3, &c.stype) +} + +func (t *TTYFileDescription) StateTypeName() string { + return "pkg/sentry/fsimpl/host.TTYFileDescription" +} + +func (t *TTYFileDescription) StateFields() []string { + return []string{ + "fileDescription", + "session", + "fgProcessGroup", + "termios", + } +} + +func (t *TTYFileDescription) beforeSave() {} + +func (t *TTYFileDescription) StateSave(stateSinkObject state.Sink) { + t.beforeSave() + stateSinkObject.Save(0, &t.fileDescription) + stateSinkObject.Save(1, &t.session) + stateSinkObject.Save(2, &t.fgProcessGroup) + stateSinkObject.Save(3, &t.termios) +} + +func (t *TTYFileDescription) afterLoad() {} + +func (t *TTYFileDescription) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &t.fileDescription) + stateSourceObject.Load(1, &t.session) + stateSourceObject.Load(2, &t.fgProcessGroup) + stateSourceObject.Load(3, &t.termios) +} + +func init() { + state.Register((*ConnectedEndpointRefs)(nil)) + state.Register((*filesystemType)(nil)) + state.Register((*filesystem)(nil)) + state.Register((*inode)(nil)) + state.Register((*fileDescription)(nil)) + state.Register((*inodeRefs)(nil)) + state.Register((*inodePlatformFile)(nil)) + state.Register((*ConnectedEndpoint)(nil)) + state.Register((*TTYFileDescription)(nil)) +} diff --git a/pkg/sentry/fsimpl/host/host_unsafe_state_autogen.go b/pkg/sentry/fsimpl/host/host_unsafe_state_autogen.go new file mode 100644 index 000000000..b2d8c661f --- /dev/null +++ b/pkg/sentry/fsimpl/host/host_unsafe_state_autogen.go @@ -0,0 +1,3 @@ +// automatically generated by stateify. + +package host diff --git a/pkg/sentry/fsimpl/host/inode_refs.go b/pkg/sentry/fsimpl/host/inode_refs.go new file mode 100644 index 000000000..17f90ce4a --- /dev/null +++ b/pkg/sentry/fsimpl/host/inode_refs.go @@ -0,0 +1,118 @@ +package host + +import ( + "fmt" + "runtime" + "sync/atomic" + + "gvisor.dev/gvisor/pkg/log" + refs_vfs1 "gvisor.dev/gvisor/pkg/refs" +) + +// ownerType is used to customize logging. Note that we use a pointer to T so +// that we do not copy the entire object when passed as a format parameter. +var inodeownerType *inode + +// Refs implements refs.RefCounter. It keeps a reference count using atomic +// operations and calls the destructor when the count reaches zero. +// +// Note that the number of references is actually refCount + 1 so that a default +// zero-value Refs object contains one reference. +// +// TODO(gvisor.dev/issue/1486): Store stack traces when leak check is enabled in +// a map with 16-bit hashes, and store the hash in the top 16 bits of refCount. +// This will allow us to add stack trace information to the leak messages +// without growing the size of Refs. +// +// +stateify savable +type inodeRefs struct { + // refCount is composed of two fields: + // + // [32-bit speculative references]:[32-bit real references] + // + // Speculative references are used for TryIncRef, to avoid a CompareAndSwap + // loop. See IncRef, DecRef and TryIncRef for details of how these fields are + // used. + refCount int64 +} + +func (r *inodeRefs) finalize() { + var note string + switch refs_vfs1.GetLeakMode() { + case refs_vfs1.NoLeakChecking: + return + case refs_vfs1.UninitializedLeakChecking: + note = "(Leak checker uninitialized): " + } + if n := r.ReadRefs(); n != 0 { + log.Warningf("%sRefs %p owned by %T garbage collected with ref count of %d (want 0)", note, r, inodeownerType, n) + } +} + +// EnableLeakCheck checks for reference leaks when Refs gets garbage collected. +func (r *inodeRefs) EnableLeakCheck() { + if refs_vfs1.GetLeakMode() != refs_vfs1.NoLeakChecking { + runtime.SetFinalizer(r, (*inodeRefs).finalize) + } +} + +// ReadRefs returns the current number of references. The returned count is +// inherently racy and is unsafe to use without external synchronization. +func (r *inodeRefs) ReadRefs() int64 { + + return atomic.LoadInt64(&r.refCount) + 1 +} + +// IncRef implements refs.RefCounter.IncRef. +// +//go:nosplit +func (r *inodeRefs) IncRef() { + if v := atomic.AddInt64(&r.refCount, 1); v <= 0 { + panic(fmt.Sprintf("Incrementing non-positive ref count %p owned by %T", r, inodeownerType)) + } +} + +// TryIncRef implements refs.RefCounter.TryIncRef. +// +// To do this safely without a loop, a speculative reference is first acquired +// on the object. This allows multiple concurrent TryIncRef calls to distinguish +// other TryIncRef calls from genuine references held. +// +//go:nosplit +func (r *inodeRefs) TryIncRef() bool { + const speculativeRef = 1 << 32 + v := atomic.AddInt64(&r.refCount, speculativeRef) + if int32(v) < 0 { + + atomic.AddInt64(&r.refCount, -speculativeRef) + return false + } + + atomic.AddInt64(&r.refCount, -speculativeRef+1) + return true +} + +// DecRef implements refs.RefCounter.DecRef. +// +// Note that speculative references are counted here. Since they were added +// prior to real references reaching zero, they will successfully convert to +// real references. In other words, we see speculative references only in the +// following case: +// +// A: TryIncRef [speculative increase => sees non-negative references] +// B: DecRef [real decrease] +// A: TryIncRef [transform speculative to real] +// +//go:nosplit +func (r *inodeRefs) DecRef(destroy func()) { + switch v := atomic.AddInt64(&r.refCount, -1); { + case v < -1: + panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %T", r, inodeownerType)) + + case v == -1: + + if destroy != nil { + destroy() + } + } +} |