diff options
Diffstat (limited to 'pkg/sentry/fsimpl/devpts')
-rw-r--r-- | pkg/sentry/fsimpl/devpts/BUILD | 61 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/devpts/devpts_state_autogen.go | 474 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/devpts/devpts_test.go | 56 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/devpts/root_inode_refs.go | 118 |
4 files changed, 592 insertions, 117 deletions
diff --git a/pkg/sentry/fsimpl/devpts/BUILD b/pkg/sentry/fsimpl/devpts/BUILD deleted file mode 100644 index 48e13613a..000000000 --- a/pkg/sentry/fsimpl/devpts/BUILD +++ /dev/null @@ -1,61 +0,0 @@ -load("//tools:defs.bzl", "go_library", "go_test") -load("//tools/go_generics:defs.bzl", "go_template_instance") - -licenses(["notice"]) - -go_template_instance( - name = "root_inode_refs", - out = "root_inode_refs.go", - package = "devpts", - prefix = "rootInode", - template = "//pkg/refs_vfs2:refs_template", - types = { - "T": "rootInode", - }, -) - -go_library( - name = "devpts", - srcs = [ - "devpts.go", - "line_discipline.go", - "master.go", - "queue.go", - "replica.go", - "root_inode_refs.go", - "terminal.go", - ], - visibility = ["//pkg/sentry:internal"], - deps = [ - "//pkg/abi/linux", - "//pkg/context", - "//pkg/log", - "//pkg/marshal", - "//pkg/marshal/primitive", - "//pkg/refs", - "//pkg/safemem", - "//pkg/sentry/arch", - "//pkg/sentry/fs/lock", - "//pkg/sentry/fsimpl/kernfs", - "//pkg/sentry/kernel", - "//pkg/sentry/kernel/auth", - "//pkg/sentry/unimpl", - "//pkg/sentry/vfs", - "//pkg/sync", - "//pkg/syserror", - "//pkg/usermem", - "//pkg/waiter", - ], -) - -go_test( - name = "devpts_test", - size = "small", - srcs = ["devpts_test.go"], - library = ":devpts", - deps = [ - "//pkg/abi/linux", - "//pkg/sentry/contexttest", - "//pkg/usermem", - ], -) diff --git a/pkg/sentry/fsimpl/devpts/devpts_state_autogen.go b/pkg/sentry/fsimpl/devpts/devpts_state_autogen.go new file mode 100644 index 000000000..d02c88a3b --- /dev/null +++ b/pkg/sentry/fsimpl/devpts/devpts_state_autogen.go @@ -0,0 +1,474 @@ +// automatically generated by stateify. + +package devpts + +import ( + "gvisor.dev/gvisor/pkg/state" +) + +func (f *FilesystemType) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.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 (f *filesystem) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.filesystem" +} + +func (f *filesystem) StateFields() []string { + return []string{ + "Filesystem", + "devMinor", + } +} + +func (f *filesystem) beforeSave() {} + +func (f *filesystem) StateSave(stateSinkObject state.Sink) { + f.beforeSave() + stateSinkObject.Save(0, &f.Filesystem) + stateSinkObject.Save(1, &f.devMinor) +} + +func (f *filesystem) afterLoad() {} + +func (f *filesystem) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &f.Filesystem) + stateSourceObject.Load(1, &f.devMinor) +} + +func (r *rootInode) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.rootInode" +} + +func (r *rootInode) StateFields() []string { + return []string{ + "implStatFS", + "AlwaysValid", + "InodeAttrs", + "InodeDirectoryNoNewChildren", + "InodeNotSymlink", + "OrderedChildren", + "rootInodeRefs", + "locks", + "dentry", + "master", + "root", + "replicas", + "nextIdx", + } +} + +func (r *rootInode) beforeSave() {} + +func (r *rootInode) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.implStatFS) + stateSinkObject.Save(1, &r.AlwaysValid) + stateSinkObject.Save(2, &r.InodeAttrs) + stateSinkObject.Save(3, &r.InodeDirectoryNoNewChildren) + stateSinkObject.Save(4, &r.InodeNotSymlink) + stateSinkObject.Save(5, &r.OrderedChildren) + stateSinkObject.Save(6, &r.rootInodeRefs) + stateSinkObject.Save(7, &r.locks) + stateSinkObject.Save(8, &r.dentry) + stateSinkObject.Save(9, &r.master) + stateSinkObject.Save(10, &r.root) + stateSinkObject.Save(11, &r.replicas) + stateSinkObject.Save(12, &r.nextIdx) +} + +func (r *rootInode) afterLoad() {} + +func (r *rootInode) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.implStatFS) + stateSourceObject.Load(1, &r.AlwaysValid) + stateSourceObject.Load(2, &r.InodeAttrs) + stateSourceObject.Load(3, &r.InodeDirectoryNoNewChildren) + stateSourceObject.Load(4, &r.InodeNotSymlink) + stateSourceObject.Load(5, &r.OrderedChildren) + stateSourceObject.Load(6, &r.rootInodeRefs) + stateSourceObject.Load(7, &r.locks) + stateSourceObject.Load(8, &r.dentry) + stateSourceObject.Load(9, &r.master) + stateSourceObject.Load(10, &r.root) + stateSourceObject.Load(11, &r.replicas) + stateSourceObject.Load(12, &r.nextIdx) +} + +func (i *implStatFS) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.implStatFS" +} + +func (i *implStatFS) StateFields() []string { + return []string{} +} + +func (i *implStatFS) beforeSave() {} + +func (i *implStatFS) StateSave(stateSinkObject state.Sink) { + i.beforeSave() +} + +func (i *implStatFS) afterLoad() {} + +func (i *implStatFS) StateLoad(stateSourceObject state.Source) { +} + +func (l *lineDiscipline) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.lineDiscipline" +} + +func (l *lineDiscipline) StateFields() []string { + return []string{ + "size", + "inQueue", + "outQueue", + "termios", + "column", + } +} + +func (l *lineDiscipline) beforeSave() {} + +func (l *lineDiscipline) StateSave(stateSinkObject state.Sink) { + l.beforeSave() + if !state.IsZeroValue(&l.masterWaiter) { + state.Failf("masterWaiter is %#v, expected zero", &l.masterWaiter) + } + if !state.IsZeroValue(&l.replicaWaiter) { + state.Failf("replicaWaiter is %#v, expected zero", &l.replicaWaiter) + } + stateSinkObject.Save(0, &l.size) + stateSinkObject.Save(1, &l.inQueue) + stateSinkObject.Save(2, &l.outQueue) + stateSinkObject.Save(3, &l.termios) + stateSinkObject.Save(4, &l.column) +} + +func (l *lineDiscipline) afterLoad() {} + +func (l *lineDiscipline) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &l.size) + stateSourceObject.Load(1, &l.inQueue) + stateSourceObject.Load(2, &l.outQueue) + stateSourceObject.Load(3, &l.termios) + stateSourceObject.Load(4, &l.column) +} + +func (o *outputQueueTransformer) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.outputQueueTransformer" +} + +func (o *outputQueueTransformer) StateFields() []string { + return []string{} +} + +func (o *outputQueueTransformer) beforeSave() {} + +func (o *outputQueueTransformer) StateSave(stateSinkObject state.Sink) { + o.beforeSave() +} + +func (o *outputQueueTransformer) afterLoad() {} + +func (o *outputQueueTransformer) StateLoad(stateSourceObject state.Source) { +} + +func (i *inputQueueTransformer) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.inputQueueTransformer" +} + +func (i *inputQueueTransformer) StateFields() []string { + return []string{} +} + +func (i *inputQueueTransformer) beforeSave() {} + +func (i *inputQueueTransformer) StateSave(stateSinkObject state.Sink) { + i.beforeSave() +} + +func (i *inputQueueTransformer) afterLoad() {} + +func (i *inputQueueTransformer) StateLoad(stateSourceObject state.Source) { +} + +func (m *masterInode) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.masterInode" +} + +func (m *masterInode) StateFields() []string { + return []string{ + "implStatFS", + "InodeAttrs", + "InodeNoopRefCount", + "InodeNotDirectory", + "InodeNotSymlink", + "locks", + "dentry", + "root", + } +} + +func (m *masterInode) beforeSave() {} + +func (m *masterInode) StateSave(stateSinkObject state.Sink) { + m.beforeSave() + stateSinkObject.Save(0, &m.implStatFS) + stateSinkObject.Save(1, &m.InodeAttrs) + stateSinkObject.Save(2, &m.InodeNoopRefCount) + stateSinkObject.Save(3, &m.InodeNotDirectory) + stateSinkObject.Save(4, &m.InodeNotSymlink) + stateSinkObject.Save(5, &m.locks) + stateSinkObject.Save(6, &m.dentry) + stateSinkObject.Save(7, &m.root) +} + +func (m *masterInode) afterLoad() {} + +func (m *masterInode) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &m.implStatFS) + stateSourceObject.Load(1, &m.InodeAttrs) + stateSourceObject.Load(2, &m.InodeNoopRefCount) + stateSourceObject.Load(3, &m.InodeNotDirectory) + stateSourceObject.Load(4, &m.InodeNotSymlink) + stateSourceObject.Load(5, &m.locks) + stateSourceObject.Load(6, &m.dentry) + stateSourceObject.Load(7, &m.root) +} + +func (m *masterFileDescription) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.masterFileDescription" +} + +func (m *masterFileDescription) StateFields() []string { + return []string{ + "vfsfd", + "FileDescriptionDefaultImpl", + "LockFD", + "inode", + "t", + } +} + +func (m *masterFileDescription) beforeSave() {} + +func (m *masterFileDescription) StateSave(stateSinkObject state.Sink) { + m.beforeSave() + stateSinkObject.Save(0, &m.vfsfd) + stateSinkObject.Save(1, &m.FileDescriptionDefaultImpl) + stateSinkObject.Save(2, &m.LockFD) + stateSinkObject.Save(3, &m.inode) + stateSinkObject.Save(4, &m.t) +} + +func (m *masterFileDescription) afterLoad() {} + +func (m *masterFileDescription) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &m.vfsfd) + stateSourceObject.Load(1, &m.FileDescriptionDefaultImpl) + stateSourceObject.Load(2, &m.LockFD) + stateSourceObject.Load(3, &m.inode) + stateSourceObject.Load(4, &m.t) +} + +func (q *queue) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.queue" +} + +func (q *queue) StateFields() []string { + return []string{ + "readBuf", + "waitBuf", + "waitBufLen", + "readable", + "transformer", + } +} + +func (q *queue) beforeSave() {} + +func (q *queue) StateSave(stateSinkObject state.Sink) { + q.beforeSave() + stateSinkObject.Save(0, &q.readBuf) + stateSinkObject.Save(1, &q.waitBuf) + stateSinkObject.Save(2, &q.waitBufLen) + stateSinkObject.Save(3, &q.readable) + stateSinkObject.Save(4, &q.transformer) +} + +func (q *queue) afterLoad() {} + +func (q *queue) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &q.readBuf) + stateSourceObject.Load(1, &q.waitBuf) + stateSourceObject.Load(2, &q.waitBufLen) + stateSourceObject.Load(3, &q.readable) + stateSourceObject.Load(4, &q.transformer) +} + +func (r *replicaInode) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.replicaInode" +} + +func (r *replicaInode) StateFields() []string { + return []string{ + "implStatFS", + "InodeAttrs", + "InodeNoopRefCount", + "InodeNotDirectory", + "InodeNotSymlink", + "locks", + "dentry", + "root", + "t", + } +} + +func (r *replicaInode) beforeSave() {} + +func (r *replicaInode) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.implStatFS) + stateSinkObject.Save(1, &r.InodeAttrs) + stateSinkObject.Save(2, &r.InodeNoopRefCount) + stateSinkObject.Save(3, &r.InodeNotDirectory) + stateSinkObject.Save(4, &r.InodeNotSymlink) + stateSinkObject.Save(5, &r.locks) + stateSinkObject.Save(6, &r.dentry) + stateSinkObject.Save(7, &r.root) + stateSinkObject.Save(8, &r.t) +} + +func (r *replicaInode) afterLoad() {} + +func (r *replicaInode) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.implStatFS) + stateSourceObject.Load(1, &r.InodeAttrs) + stateSourceObject.Load(2, &r.InodeNoopRefCount) + stateSourceObject.Load(3, &r.InodeNotDirectory) + stateSourceObject.Load(4, &r.InodeNotSymlink) + stateSourceObject.Load(5, &r.locks) + stateSourceObject.Load(6, &r.dentry) + stateSourceObject.Load(7, &r.root) + stateSourceObject.Load(8, &r.t) +} + +func (r *replicaFileDescription) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.replicaFileDescription" +} + +func (r *replicaFileDescription) StateFields() []string { + return []string{ + "vfsfd", + "FileDescriptionDefaultImpl", + "LockFD", + "inode", + } +} + +func (r *replicaFileDescription) beforeSave() {} + +func (r *replicaFileDescription) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.vfsfd) + stateSinkObject.Save(1, &r.FileDescriptionDefaultImpl) + stateSinkObject.Save(2, &r.LockFD) + stateSinkObject.Save(3, &r.inode) +} + +func (r *replicaFileDescription) afterLoad() {} + +func (r *replicaFileDescription) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.vfsfd) + stateSourceObject.Load(1, &r.FileDescriptionDefaultImpl) + stateSourceObject.Load(2, &r.LockFD) + stateSourceObject.Load(3, &r.inode) +} + +func (r *rootInodeRefs) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.rootInodeRefs" +} + +func (r *rootInodeRefs) StateFields() []string { + return []string{ + "refCount", + } +} + +func (r *rootInodeRefs) beforeSave() {} + +func (r *rootInodeRefs) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.refCount) +} + +func (r *rootInodeRefs) afterLoad() {} + +func (r *rootInodeRefs) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.refCount) +} + +func (t *Terminal) StateTypeName() string { + return "pkg/sentry/fsimpl/devpts.Terminal" +} + +func (t *Terminal) StateFields() []string { + return []string{ + "n", + "ld", + "masterKTTY", + "replicaKTTY", + } +} + +func (t *Terminal) beforeSave() {} + +func (t *Terminal) StateSave(stateSinkObject state.Sink) { + t.beforeSave() + stateSinkObject.Save(0, &t.n) + stateSinkObject.Save(1, &t.ld) + stateSinkObject.Save(2, &t.masterKTTY) + stateSinkObject.Save(3, &t.replicaKTTY) +} + +func (t *Terminal) afterLoad() {} + +func (t *Terminal) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &t.n) + stateSourceObject.Load(1, &t.ld) + stateSourceObject.Load(2, &t.masterKTTY) + stateSourceObject.Load(3, &t.replicaKTTY) +} + +func init() { + state.Register((*FilesystemType)(nil)) + state.Register((*filesystem)(nil)) + state.Register((*rootInode)(nil)) + state.Register((*implStatFS)(nil)) + state.Register((*lineDiscipline)(nil)) + state.Register((*outputQueueTransformer)(nil)) + state.Register((*inputQueueTransformer)(nil)) + state.Register((*masterInode)(nil)) + state.Register((*masterFileDescription)(nil)) + state.Register((*queue)(nil)) + state.Register((*replicaInode)(nil)) + state.Register((*replicaFileDescription)(nil)) + state.Register((*rootInodeRefs)(nil)) + state.Register((*Terminal)(nil)) +} diff --git a/pkg/sentry/fsimpl/devpts/devpts_test.go b/pkg/sentry/fsimpl/devpts/devpts_test.go deleted file mode 100644 index 448390cfe..000000000 --- a/pkg/sentry/fsimpl/devpts/devpts_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package devpts - -import ( - "testing" - - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/sentry/contexttest" - "gvisor.dev/gvisor/pkg/usermem" -) - -func TestSimpleMasterToReplica(t *testing.T) { - ld := newLineDiscipline(linux.DefaultReplicaTermios) - ctx := contexttest.Context(t) - inBytes := []byte("hello, tty\n") - src := usermem.BytesIOSequence(inBytes) - outBytes := make([]byte, 32) - dst := usermem.BytesIOSequence(outBytes) - - // Write to the input queue. - nw, err := ld.inputQueueWrite(ctx, src) - if err != nil { - t.Fatalf("error writing to input queue: %v", err) - } - if nw != int64(len(inBytes)) { - t.Fatalf("wrote wrong length: got %d, want %d", nw, len(inBytes)) - } - - // Read from the input queue. - nr, err := ld.inputQueueRead(ctx, dst) - if err != nil { - t.Fatalf("error reading from input queue: %v", err) - } - if nr != int64(len(inBytes)) { - t.Fatalf("read wrong length: got %d, want %d", nr, len(inBytes)) - } - - outStr := string(outBytes[:nr]) - inStr := string(inBytes) - if outStr != inStr { - t.Fatalf("written and read strings do not match: got %q, want %q", outStr, inStr) - } -} diff --git a/pkg/sentry/fsimpl/devpts/root_inode_refs.go b/pkg/sentry/fsimpl/devpts/root_inode_refs.go new file mode 100644 index 000000000..051801202 --- /dev/null +++ b/pkg/sentry/fsimpl/devpts/root_inode_refs.go @@ -0,0 +1,118 @@ +package devpts + +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 rootInodeownerType *rootInode + +// 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 rootInodeRefs 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 *rootInodeRefs) 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, rootInodeownerType, n) + } +} + +// EnableLeakCheck checks for reference leaks when Refs gets garbage collected. +func (r *rootInodeRefs) EnableLeakCheck() { + if refs_vfs1.GetLeakMode() != refs_vfs1.NoLeakChecking { + runtime.SetFinalizer(r, (*rootInodeRefs).finalize) + } +} + +// ReadRefs returns the current number of references. The returned count is +// inherently racy and is unsafe to use without external synchronization. +func (r *rootInodeRefs) ReadRefs() int64 { + + return atomic.LoadInt64(&r.refCount) + 1 +} + +// IncRef implements refs.RefCounter.IncRef. +// +//go:nosplit +func (r *rootInodeRefs) IncRef() { + if v := atomic.AddInt64(&r.refCount, 1); v <= 0 { + panic(fmt.Sprintf("Incrementing non-positive ref count %p owned by %T", r, rootInodeownerType)) + } +} + +// 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 *rootInodeRefs) 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 *rootInodeRefs) 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, rootInodeownerType)) + + case v == -1: + + if destroy != nil { + destroy() + } + } +} |