diff options
Diffstat (limited to 'pkg/sentry/kernel/shm')
-rw-r--r-- | pkg/sentry/kernel/shm/BUILD | 46 | ||||
-rw-r--r-- | pkg/sentry/kernel/shm/shm_refs.go | 140 | ||||
-rw-r--r-- | pkg/sentry/kernel/shm/shm_state_autogen.go | 147 |
3 files changed, 287 insertions, 46 deletions
diff --git a/pkg/sentry/kernel/shm/BUILD b/pkg/sentry/kernel/shm/BUILD deleted file mode 100644 index 1c3c0794f..000000000 --- a/pkg/sentry/kernel/shm/BUILD +++ /dev/null @@ -1,46 +0,0 @@ -load("//tools:defs.bzl", "go_library") -load("//tools/go_generics:defs.bzl", "go_template_instance") - -package(licenses = ["notice"]) - -go_template_instance( - name = "shm_refs", - out = "shm_refs.go", - consts = { - "enableLogging": "true", - }, - package = "shm", - prefix = "Shm", - template = "//pkg/refsvfs2:refs_template", - types = { - "T": "Shm", - }, -) - -go_library( - name = "shm", - srcs = [ - "device.go", - "shm.go", - "shm_refs.go", - ], - visibility = ["//pkg/sentry:internal"], - deps = [ - "//pkg/abi/linux", - "//pkg/context", - "//pkg/hostarch", - "//pkg/log", - "//pkg/refs", - "//pkg/refsvfs2", - "//pkg/sentry/device", - "//pkg/sentry/fs", - "//pkg/sentry/kernel/auth", - "//pkg/sentry/kernel/time", - "//pkg/sentry/memmap", - "//pkg/sentry/pgalloc", - "//pkg/sentry/usage", - "//pkg/sync", - "//pkg/syserror", - "//pkg/usermem", - ], -) diff --git a/pkg/sentry/kernel/shm/shm_refs.go b/pkg/sentry/kernel/shm/shm_refs.go new file mode 100644 index 000000000..f832c1a99 --- /dev/null +++ b/pkg/sentry/kernel/shm/shm_refs.go @@ -0,0 +1,140 @@ +package shm + +import ( + "fmt" + "sync/atomic" + + "gvisor.dev/gvisor/pkg/refsvfs2" +) + +// enableLogging indicates whether reference-related events should be logged (with +// stack traces). This is false by default and should only be set to true for +// debugging purposes, as it can generate an extremely large amount of output +// and drastically degrade performance. +const ShmenableLogging = true + +// obj 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 Shmobj *Shm + +// Refs implements refs.RefCounter. It keeps a reference count using atomic +// operations and calls the destructor when the count reaches zero. +// +// NOTE: Do not introduce additional fields to the Refs struct. It is used by +// many filesystem objects, and we want to keep it as small as possible (i.e., +// the same size as using an int64 directly) to avoid taking up extra cache +// space. In general, this template should not be extended at the cost of +// performance. If it does not offer enough flexibility for a particular object +// (example: b/187877947), we should implement the RefCounter/CheckedObject +// interfaces manually. +// +// +stateify savable +type ShmRefs 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 +} + +// InitRefs initializes r with one reference and, if enabled, activates leak +// checking. +func (r *ShmRefs) InitRefs() { + atomic.StoreInt64(&r.refCount, 1) + refsvfs2.Register(r) +} + +// RefType implements refsvfs2.CheckedObject.RefType. +func (r *ShmRefs) RefType() string { + return fmt.Sprintf("%T", Shmobj)[1:] +} + +// LeakMessage implements refsvfs2.CheckedObject.LeakMessage. +func (r *ShmRefs) LeakMessage() string { + return fmt.Sprintf("[%s %p] reference count of %d instead of 0", r.RefType(), r, r.ReadRefs()) +} + +// LogRefs implements refsvfs2.CheckedObject.LogRefs. +func (r *ShmRefs) LogRefs() bool { + return ShmenableLogging +} + +// ReadRefs returns the current number of references. The returned count is +// inherently racy and is unsafe to use without external synchronization. +func (r *ShmRefs) ReadRefs() int64 { + return atomic.LoadInt64(&r.refCount) +} + +// IncRef implements refs.RefCounter.IncRef. +// +//go:nosplit +func (r *ShmRefs) IncRef() { + v := atomic.AddInt64(&r.refCount, 1) + if ShmenableLogging { + refsvfs2.LogIncRef(r, v) + } + if v <= 1 { + panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType())) + } +} + +// 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 *ShmRefs) TryIncRef() bool { + const speculativeRef = 1 << 32 + if v := atomic.AddInt64(&r.refCount, speculativeRef); int32(v) == 0 { + + atomic.AddInt64(&r.refCount, -speculativeRef) + return false + } + + v := atomic.AddInt64(&r.refCount, -speculativeRef+1) + if ShmenableLogging { + refsvfs2.LogTryIncRef(r, v) + } + 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 *ShmRefs) DecRef(destroy func()) { + v := atomic.AddInt64(&r.refCount, -1) + if ShmenableLogging { + refsvfs2.LogDecRef(r, v) + } + switch { + case v < 0: + panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType())) + + case v == 0: + refsvfs2.Unregister(r) + + if destroy != nil { + destroy() + } + } +} + +func (r *ShmRefs) afterLoad() { + if r.ReadRefs() > 0 { + refsvfs2.Register(r) + } +} diff --git a/pkg/sentry/kernel/shm/shm_state_autogen.go b/pkg/sentry/kernel/shm/shm_state_autogen.go new file mode 100644 index 000000000..30ceeaa03 --- /dev/null +++ b/pkg/sentry/kernel/shm/shm_state_autogen.go @@ -0,0 +1,147 @@ +// automatically generated by stateify. + +package shm + +import ( + "gvisor.dev/gvisor/pkg/state" +) + +func (r *Registry) StateTypeName() string { + return "pkg/sentry/kernel/shm.Registry" +} + +func (r *Registry) StateFields() []string { + return []string{ + "userNS", + "shms", + "keysToShms", + "totalPages", + "lastIDUsed", + } +} + +func (r *Registry) beforeSave() {} + +// +checklocksignore +func (r *Registry) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.userNS) + stateSinkObject.Save(1, &r.shms) + stateSinkObject.Save(2, &r.keysToShms) + stateSinkObject.Save(3, &r.totalPages) + stateSinkObject.Save(4, &r.lastIDUsed) +} + +func (r *Registry) afterLoad() {} + +// +checklocksignore +func (r *Registry) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.userNS) + stateSourceObject.Load(1, &r.shms) + stateSourceObject.Load(2, &r.keysToShms) + stateSourceObject.Load(3, &r.totalPages) + stateSourceObject.Load(4, &r.lastIDUsed) +} + +func (s *Shm) StateTypeName() string { + return "pkg/sentry/kernel/shm.Shm" +} + +func (s *Shm) StateFields() []string { + return []string{ + "ShmRefs", + "mfp", + "registry", + "ID", + "creator", + "size", + "effectiveSize", + "fr", + "key", + "perms", + "owner", + "attachTime", + "detachTime", + "changeTime", + "creatorPID", + "lastAttachDetachPID", + "pendingDestruction", + } +} + +func (s *Shm) beforeSave() {} + +// +checklocksignore +func (s *Shm) StateSave(stateSinkObject state.Sink) { + s.beforeSave() + stateSinkObject.Save(0, &s.ShmRefs) + stateSinkObject.Save(1, &s.mfp) + stateSinkObject.Save(2, &s.registry) + stateSinkObject.Save(3, &s.ID) + stateSinkObject.Save(4, &s.creator) + stateSinkObject.Save(5, &s.size) + stateSinkObject.Save(6, &s.effectiveSize) + stateSinkObject.Save(7, &s.fr) + stateSinkObject.Save(8, &s.key) + stateSinkObject.Save(9, &s.perms) + stateSinkObject.Save(10, &s.owner) + stateSinkObject.Save(11, &s.attachTime) + stateSinkObject.Save(12, &s.detachTime) + stateSinkObject.Save(13, &s.changeTime) + stateSinkObject.Save(14, &s.creatorPID) + stateSinkObject.Save(15, &s.lastAttachDetachPID) + stateSinkObject.Save(16, &s.pendingDestruction) +} + +func (s *Shm) afterLoad() {} + +// +checklocksignore +func (s *Shm) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &s.ShmRefs) + stateSourceObject.Load(1, &s.mfp) + stateSourceObject.Load(2, &s.registry) + stateSourceObject.Load(3, &s.ID) + stateSourceObject.Load(4, &s.creator) + stateSourceObject.Load(5, &s.size) + stateSourceObject.Load(6, &s.effectiveSize) + stateSourceObject.Load(7, &s.fr) + stateSourceObject.Load(8, &s.key) + stateSourceObject.Load(9, &s.perms) + stateSourceObject.Load(10, &s.owner) + stateSourceObject.Load(11, &s.attachTime) + stateSourceObject.Load(12, &s.detachTime) + stateSourceObject.Load(13, &s.changeTime) + stateSourceObject.Load(14, &s.creatorPID) + stateSourceObject.Load(15, &s.lastAttachDetachPID) + stateSourceObject.Load(16, &s.pendingDestruction) +} + +func (r *ShmRefs) StateTypeName() string { + return "pkg/sentry/kernel/shm.ShmRefs" +} + +func (r *ShmRefs) StateFields() []string { + return []string{ + "refCount", + } +} + +func (r *ShmRefs) beforeSave() {} + +// +checklocksignore +func (r *ShmRefs) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.refCount) +} + +// +checklocksignore +func (r *ShmRefs) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.refCount) + stateSourceObject.AfterLoad(r.afterLoad) +} + +func init() { + state.Register((*Registry)(nil)) + state.Register((*Shm)(nil)) + state.Register((*ShmRefs)(nil)) +} |