summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorZyad A. Ali <zyad.ali.me@gmail.com>2021-07-30 16:57:59 +0200
committerZyad A. Ali <zyad.ali.me@gmail.com>2021-09-28 20:43:52 +0200
commit13d36561b8a9cab6cf20b4b5053752955f451518 (patch)
treef2be0923e303b147beb251b4b77be8a66e52605d
parent7508a0efeeef19a3e08e06e80be8258743438412 (diff)
Return FDs in RegistryImpl functions and use Views.
Update RegistryImpl functions to return file descriptions, instead of queues, and use Views in queue inodes. Updates #136
-rw-r--r--pkg/sentry/fsimpl/mqfs/BUILD1
-rw-r--r--pkg/sentry/fsimpl/mqfs/registry.go75
-rw-r--r--pkg/sentry/kernel/mq/BUILD2
-rw-r--r--pkg/sentry/kernel/mq/mq.go64
4 files changed, 122 insertions, 20 deletions
diff --git a/pkg/sentry/fsimpl/mqfs/BUILD b/pkg/sentry/fsimpl/mqfs/BUILD
index e688b9b48..ef843015d 100644
--- a/pkg/sentry/fsimpl/mqfs/BUILD
+++ b/pkg/sentry/fsimpl/mqfs/BUILD
@@ -29,6 +29,7 @@ go_library(
"//pkg/context",
"//pkg/errors/linuxerr",
"//pkg/refsvfs2",
+ "//pkg/sentry/fs",
"//pkg/sentry/fsimpl/kernfs",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/ipc",
diff --git a/pkg/sentry/fsimpl/mqfs/registry.go b/pkg/sentry/fsimpl/mqfs/registry.go
index 9361b7eb4..89ffaaf04 100644
--- a/pkg/sentry/fsimpl/mqfs/registry.go
+++ b/pkg/sentry/fsimpl/mqfs/registry.go
@@ -17,6 +17,8 @@ package mqfs
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/mq"
@@ -78,20 +80,32 @@ func NewRegistryImpl(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *
}, nil
}
-// Lookup implements mq.RegistryImpl.Lookup.
-func (r *RegistryImpl) Lookup(ctx context.Context, name string) *mq.Queue {
+// Get implements mq.RegistryImpl.Get.
+func (r *RegistryImpl) Get(ctx context.Context, name string, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, bool, error) {
r.mu.Lock()
defer r.mu.Unlock()
inode, err := r.lookup(ctx, name)
if err != nil {
- return nil
+ return nil, false, nil
}
- return inode.(*queueInode).queue
+
+ qInode := inode.(*queueInode)
+ if !qInode.queue.HasPermissions(auth.CredentialsFromContext(ctx), perm(rOnly, wOnly, readWrite)) {
+ // "The queue exists, but the caller does not have permission to
+ // open it in the specified mode."
+ return nil, false, linuxerr.EACCES
+ }
+
+ fd, err := r.newFD(qInode.queue, qInode, rOnly, wOnly, readWrite, block, flags)
+ if err != nil {
+ return nil, false, err
+ }
+ return fd, true, nil
}
// New implements mq.RegistryImpl.New.
-func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, perm linux.FileMode) (*vfs.FileDescription, error) {
+func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, rOnly, wOnly, readWrite, block bool, perm linux.FileMode, flags uint32) (*vfs.FileDescription, error) {
r.mu.Lock()
defer r.mu.Unlock()
@@ -101,13 +115,7 @@ func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, perm l
if err != nil {
return nil, err
}
-
- fd := &queueFD{queue: q}
- err = fd.Init(r.mount, r.root, q, qInode.Locks(), 0 /* flags */)
- if err != nil {
- return nil, err
- }
- return &fd.vfsfd, nil
+ return r.newFD(q, qInode, rOnly, wOnly, readWrite, block, flags)
}
// Unlink implements mq.RegistryImpl.Unlink.
@@ -115,6 +123,11 @@ func (r *RegistryImpl) Unlink(ctx context.Context, name string) error {
r.mu.Lock()
defer r.mu.Unlock()
+ creds := auth.CredentialsFromContext(ctx)
+ if err := r.root.Inode().CheckPermissions(ctx, creds, vfs.MayWrite|vfs.MayExec); err != nil {
+ return err
+ }
+
root := r.root.Inode().(*rootInode)
inode, err := r.lookup(ctx, name)
if err != nil {
@@ -123,6 +136,11 @@ func (r *RegistryImpl) Unlink(ctx context.Context, name string) error {
return root.Unlink(ctx, name, inode)
}
+// Destroy implements mq.RegistryImpl.Destroy.
+func (r *RegistryImpl) Destroy(ctx context.Context) {
+ r.root.DecRef(ctx)
+}
+
// lookup retreives a kernfs.Inode using a name.
//
// Precondition: r.mu must be held.
@@ -135,7 +153,34 @@ func (r *RegistryImpl) lookup(ctx context.Context, name string) (kernfs.Inode, e
return lookup, nil
}
-// Destroy implements mq.RegistryImpl.Destroy.
-func (r *RegistryImpl) Destroy(ctx context.Context) {
- r.root.DecRef(ctx)
+// newFD returns a new file description created using the given queue and inode.
+func (r *RegistryImpl) newFD(q *mq.Queue, inode *queueInode, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, error) {
+ view, err := mq.NewView(q, rOnly, wOnly, readWrite, block)
+ if err != nil {
+ return nil, err
+ }
+
+ var dentry kernfs.Dentry
+ dentry.Init(&r.fs.Filesystem, inode)
+
+ fd := &queueFD{queue: view}
+ err = fd.Init(r.mount, &dentry, inode.queue, inode.Locks(), flags)
+ if err != nil {
+ return nil, err
+ }
+ return &fd.vfsfd, nil
+}
+
+// perm returns a permission mask created using given flags.
+func perm(rOnly, wOnly, readWrite bool) fs.PermMask {
+ switch {
+ case readWrite:
+ return fs.PermMask{Read: true, Write: true}
+ case wOnly:
+ return fs.PermMask{Write: true}
+ case rOnly:
+ return fs.PermMask{Read: true}
+ default:
+ return fs.PermMask{} // Can't happen, see NewView.
+ }
}
diff --git a/pkg/sentry/kernel/mq/BUILD b/pkg/sentry/kernel/mq/BUILD
index 7b00b8346..fefac3ba5 100644
--- a/pkg/sentry/kernel/mq/BUILD
+++ b/pkg/sentry/kernel/mq/BUILD
@@ -25,7 +25,9 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/sentry/fs",
+ "//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
"//pkg/sync",
"//pkg/waiter",
diff --git a/pkg/sentry/kernel/mq/mq.go b/pkg/sentry/kernel/mq/mq.go
index 954883c5f..217478dca 100644
--- a/pkg/sentry/kernel/mq/mq.go
+++ b/pkg/sentry/kernel/mq/mq.go
@@ -21,13 +21,16 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
+ "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/waiter"
)
const (
+ MaxName = 255 // Maximum size for a queue name.
maxPriority = linux.MQ_PRIO_MAX - 1 // Highest possible message priority.
)
@@ -48,18 +51,20 @@ type Registry struct {
// Registry to avoid dealing directly with the filesystem. RegistryImpl should
// be implemented by mqfs and provided to Registry at initialization.
type RegistryImpl interface {
- // Lookup returns the queue with the given name, nil if non exists.
- Lookup(context.Context, string) *Queue
+ // Get searchs for a queue with the given name, if it exists, the queue is
+ // used to create a new FD, return it and return true. If the queue doesn't
+ // exist, return false and no error. An error is returned if creation fails.
+ Get(ctx context.Context, name string, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, bool, error)
// New creates a new inode and file description using the given queue,
- // inserts the inode into the filesystem tree with the given name, and
+ // inserts the inode into the filesystem tree using the given name, and
// returns the file description. An error is returned if creation fails, or
// if the name already exists.
- New(context.Context, string, *Queue, linux.FileMode) (*vfs.FileDescription, error)
+ New(ctx context.Context, name string, q *Queue, rOnly, wOnly, readWrite, block bool, perm linux.FileMode, flags uint32) (*vfs.FileDescription, error)
// Unlink removes the queue with given name from the registry, and returns
// an error if the name doesn't exist.
- Unlink(context.Context, string) error
+ Unlink(ctx context.Context, name string) error
// Destroy destroys the registry.
Destroy(context.Context)
@@ -144,6 +149,43 @@ type View interface {
waiter.Waitable
}
+// ReaderWriter provides a send and receive view into a queue.
+type ReaderWriter struct {
+ *Queue
+
+ block bool
+}
+
+// Reader provides a send-only view into a queue.
+type Reader struct {
+ *Queue
+
+ block bool
+}
+
+// Writer provides a receive-only view into a queue.
+type Writer struct {
+ *Queue
+
+ block bool
+}
+
+// NewView creates a new view into a queue and returns it.
+func NewView(q *Queue, rOnly, wOnly, readWrite, block bool) (View, error) {
+ switch {
+ case readWrite:
+ return ReaderWriter{Queue: q, block: block}, nil
+ case wOnly:
+ return Writer{Queue: q, block: block}, nil
+ case rOnly:
+ return Reader{Queue: q, block: block}, nil
+ default:
+ // This case can't happen, due to O_RDONLY flag being 0 and O_WRONLY
+ // being 1, so one of them must be true.
+ return nil, linuxerr.EINVAL
+ }
+}
+
// Message holds a message exchanged through a Queue via mq_timedsend(2) and
// mq_timedreceive(2), and additional info relating to the message.
//
@@ -243,3 +285,15 @@ func (q *Queue) EventUnregister(e *waiter.Entry) {
q.senders.EventUnregister(e)
q.receivers.EventUnregister(e)
}
+
+// HasPermissions returns true if the given credentials meet the access
+// permissions required by the queue.
+func (q *Queue) HasPermissions(creds *auth.Credentials, req fs.PermMask) bool {
+ p := q.perms.Other
+ if q.owner.UID == creds.EffectiveKUID {
+ p = q.perms.User
+ } else if creds.InGroup(q.owner.GID) {
+ p = q.perms.Group
+ }
+ return p.SupersetOf(req)
+}