From 7c488fcfe8a5fd17f99f1c88c29ce97f5851f786 Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Mon, 31 May 2021 17:31:40 +0200 Subject: Create package msgqueue. Create package msgqueue, define primitives to be used for message queues, and add a msgqueue.Registry to IPCNamespace. Updates #135 --- pkg/sentry/kernel/msgqueue/msgqueue.go | 111 +++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 pkg/sentry/kernel/msgqueue/msgqueue.go (limited to 'pkg/sentry/kernel/msgqueue/msgqueue.go') diff --git a/pkg/sentry/kernel/msgqueue/msgqueue.go b/pkg/sentry/kernel/msgqueue/msgqueue.go new file mode 100644 index 000000000..51db8b12c --- /dev/null +++ b/pkg/sentry/kernel/msgqueue/msgqueue.go @@ -0,0 +1,111 @@ +// Copyright 2021 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 msgqueue implements System V message queues. +package msgqueue + +import ( + "sync" + + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/sentry/kernel/ipc" + ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" + "gvisor.dev/gvisor/pkg/waiter" +) + +// Registry contains a set of message queues that can be referenced using keys +// or IDs. +// +// +stateify savable +type Registry struct { + // mu protects all the fields below. + mu sync.Mutex `state:"nosave"` + + // reg defines basic fields and operations needed for all SysV registries. + reg *ipc.Registry +} + +// Queue represents a SysV message queue, described by sysvipc(7). +// +// +stateify savable +type Queue struct { + // registry is the registry owning this queue. Immutable. + registry *Registry + + // mu protects all the fields below. + mu sync.Mutex `state:"nosave"` + + // obj defines basic fields that should be included in all SysV IPC objects. + obj *ipc.Object + + // senders holds a queue of blocked message senders. Senders are notified + // when enough space is available in the queue to insert their message. + senders waiter.Queue + + // receivers holds a queue of blocked receivers. Receivers are notified + // when a new message is inserted into the queue and can be received. + receivers waiter.Queue + + // messages is a list of sent messages. + messages msgList + + // sendTime is the last time a msgsnd was perfomed. + sendTime ktime.Time + + // receiveTime is the last time a msgrcv was performed. + receiveTime ktime.Time + + // changeTime is the last time the queue was modified using msgctl. + changeTime ktime.Time + + // byteCount is the current number of message bytes in the queue. + byteCount uint64 + + // messageCount is the current number of messages in the queue. + messageCount uint64 + + // maxBytes is the maximum allowed number of bytes in the queue, and is also + // used as a limit for the number of total possible messages. + maxBytes uint64 + + // sendPID is the PID of the process that performed the last msgsnd. + sendPID int32 + + // receivePID is the PID of the process that performed the last msgrcv. + receivePID int32 +} + +// Message represents a message exchanged through a Queue via msgsnd(2) and +// msgrcv(2). +// +// +stateify savable +type Message struct { + msgEntry + + // mType is an integer representing the type of the sent message. + mType int64 + + // mText is an untyped block of memory. + mText []byte + + // mSize is the size of mText. + mSize uint64 +} + +// NewRegistry returns a new Registry ready to be used. +func NewRegistry(userNS *auth.UserNamespace) *Registry { + return &Registry{ + reg: ipc.NewRegistry(userNS), + } +} -- cgit v1.2.3 From 7eae6402c111323cd6e92e0499dfa12cf2554d1a Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Mon, 31 May 2021 21:07:47 +0200 Subject: Implement Registry.FindOrCreate. FindOrCreate implements the behaviour of msgget(2). Updates #135 --- pkg/sentry/kernel/msgqueue/BUILD | 7 ++- pkg/sentry/kernel/msgqueue/msgqueue.go | 93 +++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 7 deletions(-) (limited to 'pkg/sentry/kernel/msgqueue/msgqueue.go') diff --git a/pkg/sentry/kernel/msgqueue/BUILD b/pkg/sentry/kernel/msgqueue/BUILD index dfe937e78..4114ee45a 100644 --- a/pkg/sentry/kernel/msgqueue/BUILD +++ b/pkg/sentry/kernel/msgqueue/BUILD @@ -23,10 +23,15 @@ go_library( ], visibility = ["//pkg/sentry:internal"], deps = [ + "//pkg/abi/linux", + "//pkg/context", + "//pkg/errors/linuxerr", + "//pkg/log", + "//pkg/sentry/fs", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/ipc", "//pkg/sentry/kernel/time", - "//pkg/waiter", "//pkg/sync", + "//pkg/waiter", ], ) diff --git a/pkg/sentry/kernel/msgqueue/msgqueue.go b/pkg/sentry/kernel/msgqueue/msgqueue.go index 51db8b12c..0e2d996a8 100644 --- a/pkg/sentry/kernel/msgqueue/msgqueue.go +++ b/pkg/sentry/kernel/msgqueue/msgqueue.go @@ -16,14 +16,28 @@ package msgqueue import ( - "sync" - + "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/kernel/ipc" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" + "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/waiter" ) +const ( + // System-wide limit for maximum number of queues. + maxQueues = linux.MSGMNI + + // Maximum size of a queue in bytes. + maxQueueBytes = linux.MSGMNB + + // Maximum size of a message in bytes. + maxMessageBytes = linux.MSGMAX +) + // Registry contains a set of message queues that can be referenced using keys // or IDs. // @@ -36,6 +50,13 @@ type Registry struct { reg *ipc.Registry } +// NewRegistry returns a new Registry ready to be used. +func NewRegistry(userNS *auth.UserNamespace) *Registry { + return &Registry{ + reg: ipc.NewRegistry(userNS), + } +} + // Queue represents a SysV message queue, described by sysvipc(7). // // +stateify savable @@ -103,9 +124,69 @@ type Message struct { mSize uint64 } -// NewRegistry returns a new Registry ready to be used. -func NewRegistry(userNS *auth.UserNamespace) *Registry { - return &Registry{ - reg: ipc.NewRegistry(userNS), +// FindOrCreate creates a new message queue or returns an existing one. See +// msgget(2). +func (r *Registry) FindOrCreate(ctx context.Context, key ipc.Key, mode linux.FileMode, private, create, exclusive bool) (*Queue, error) { + r.mu.Lock() + defer r.mu.Unlock() + + if !private { + queue, err := r.reg.Find(ctx, key, mode, create, exclusive) + if err != nil { + return nil, err + } + + if queue != nil { + return queue.(*Queue), nil + } + } + + // Check system-wide limits. + if r.reg.ObjectCount() >= maxQueues { + return nil, linuxerr.ENOSPC } + + return r.newQueueLocked(ctx, key, fs.FileOwnerFromContext(ctx), fs.FilePermsFromMode(mode)) +} + +// newQueueLocked creates a new queue using the given fields. An error is +// returned if there're no more available identifiers. +// +// Precondition: r.mu must be held. +func (r *Registry) newQueueLocked(ctx context.Context, key ipc.Key, creator fs.FileOwner, perms fs.FilePermissions) (*Queue, error) { + q := &Queue{ + registry: r, + obj: ipc.NewObject(r.reg.UserNS, key, creator, creator, perms), + sendTime: ktime.ZeroTime, + receiveTime: ktime.ZeroTime, + changeTime: ktime.NowFromContext(ctx), + maxBytes: maxQueueBytes, + } + + err := r.reg.Register(q) + if err != nil { + return nil, err + } + return q, nil +} + +// Lock implements ipc.Mechanism.Lock. +func (q *Queue) Lock() { + q.mu.Lock() +} + +// Unlock implements ipc.mechanism.Unlock. +// +// +checklocksignore +func (q *Queue) Unlock() { + q.mu.Unlock() +} + +// Object implements ipc.Mechanism.Object. +func (q *Queue) Object() *ipc.Object { + return q.obj +} + +// Destroy implements ipc.Mechanism.Destroy. It's yet to be implemented. +func (q *Queue) Destroy() { } -- cgit v1.2.3 From 084aa4fa51b74b426cf1bc0e1347624b2b516bcd Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Wed, 2 Jun 2021 15:33:36 +0200 Subject: Implement Registry.Remove. Remove implements the behaviour or msgctl(IPC_RMID). Updates #135 --- pkg/sentry/kernel/msgqueue/BUILD | 1 - pkg/sentry/kernel/msgqueue/msgqueue.go | 25 ++++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'pkg/sentry/kernel/msgqueue/msgqueue.go') diff --git a/pkg/sentry/kernel/msgqueue/BUILD b/pkg/sentry/kernel/msgqueue/BUILD index 4114ee45a..e4305fead 100644 --- a/pkg/sentry/kernel/msgqueue/BUILD +++ b/pkg/sentry/kernel/msgqueue/BUILD @@ -26,7 +26,6 @@ go_library( "//pkg/abi/linux", "//pkg/context", "//pkg/errors/linuxerr", - "//pkg/log", "//pkg/sentry/fs", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/ipc", diff --git a/pkg/sentry/kernel/msgqueue/msgqueue.go b/pkg/sentry/kernel/msgqueue/msgqueue.go index 0e2d996a8..5f7f35cd3 100644 --- a/pkg/sentry/kernel/msgqueue/msgqueue.go +++ b/pkg/sentry/kernel/msgqueue/msgqueue.go @@ -67,6 +67,11 @@ type Queue struct { // mu protects all the fields below. mu sync.Mutex `state:"nosave"` + // dead is set to true when a queue is removed from the registry and should + // not be used. Operations on the queue should check dead, and return + // EIDRM if set to true. + dead bool + // obj defines basic fields that should be included in all SysV IPC objects. obj *ipc.Object @@ -170,6 +175,17 @@ func (r *Registry) newQueueLocked(ctx context.Context, key ipc.Key, creator fs.F return q, nil } +// Remove removes the queue with specified ID. All waiters (readers and +// writers) and writers will be awakened and fail. Remove will return an error +// if the ID is invalid, or the the user doesn't have privileges. +func (r *Registry) Remove(id ipc.ID, creds *auth.Credentials) error { + r.mu.Lock() + defer r.mu.Unlock() + + r.reg.Remove(id, creds) + return nil +} + // Lock implements ipc.Mechanism.Lock. func (q *Queue) Lock() { q.mu.Lock() @@ -187,6 +203,13 @@ func (q *Queue) Object() *ipc.Object { return q.obj } -// Destroy implements ipc.Mechanism.Destroy. It's yet to be implemented. +// Destroy implements ipc.Mechanism.Destroy. func (q *Queue) Destroy() { + q.dead = true + + // Notify waiters. Senders and receivers will try to run, and return an + // error (EIDRM). Waiters should remove themselves from the queue after + // waking up. + q.senders.Notify(waiter.EventOut) + q.receivers.Notify(waiter.EventIn) } -- cgit v1.2.3 From 4a874557f5846ec9cb2e5b2515921e37a4ca4f07 Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Wed, 2 Jun 2021 15:54:36 +0200 Subject: Implement stubs for msgget(2) and msgctl(IPC_RMID). Add support for msgget, and msgctl(IPC_RMID), and enable msgqueue syscall tests. Updates #135 --- pkg/sentry/kernel/msgqueue/msgqueue.go | 5 +++ pkg/sentry/syscalls/linux/BUILD | 1 + pkg/sentry/syscalls/linux/linux64.go | 16 ++++----- pkg/sentry/syscalls/linux/sys_msgqueue.go | 57 +++++++++++++++++++++++++++++++ test/syscalls/linux/msgqueue.cc | 9 ----- 5 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 pkg/sentry/syscalls/linux/sys_msgqueue.go (limited to 'pkg/sentry/kernel/msgqueue/msgqueue.go') diff --git a/pkg/sentry/kernel/msgqueue/msgqueue.go b/pkg/sentry/kernel/msgqueue/msgqueue.go index 5f7f35cd3..3ce926950 100644 --- a/pkg/sentry/kernel/msgqueue/msgqueue.go +++ b/pkg/sentry/kernel/msgqueue/msgqueue.go @@ -213,3 +213,8 @@ func (q *Queue) Destroy() { q.senders.Notify(waiter.EventOut) q.receivers.Notify(waiter.EventIn) } + +// ID returns queue's ID. +func (q *Queue) ID() ipc.ID { + return q.obj.ID +} diff --git a/pkg/sentry/syscalls/linux/BUILD b/pkg/sentry/syscalls/linux/BUILD index 7941b2be9..ccccce6a9 100644 --- a/pkg/sentry/syscalls/linux/BUILD +++ b/pkg/sentry/syscalls/linux/BUILD @@ -25,6 +25,7 @@ go_library( "sys_mempolicy.go", "sys_mmap.go", "sys_mount.go", + "sys_msgqueue.go", "sys_pipe.go", "sys_poll.go", "sys_prctl.go", diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index f1cb5a2c8..6f44d767b 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -121,10 +121,10 @@ var AMD64 = &kernel.SyscallTable{ 65: syscalls.PartiallySupported("semop", Semop, "Option SEM_UNDO not supported.", nil), 66: syscalls.Supported("semctl", Semctl), 67: syscalls.Supported("shmdt", Shmdt), - 68: syscalls.ErrorWithEvent("msgget", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) - 69: syscalls.ErrorWithEvent("msgsnd", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) - 70: syscalls.ErrorWithEvent("msgrcv", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) - 71: syscalls.ErrorWithEvent("msgctl", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 68: syscalls.Supported("msgget", Msgget), + 69: syscalls.ErrorWithEvent("msgsnd", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 70: syscalls.ErrorWithEvent("msgrcv", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 71: syscalls.PartiallySupported("msgctl", Msgctl, "Only supports IPC_RMID option.", []string{"gvisor.dev/issue/135"}), 72: syscalls.PartiallySupported("fcntl", Fcntl, "Not all options are supported.", nil), 73: syscalls.PartiallySupported("flock", Flock, "Locks are held within the sandbox only.", nil), 74: syscalls.PartiallySupported("fsync", Fsync, "Full data flush is not guaranteed at this time.", nil), @@ -616,10 +616,10 @@ var ARM64 = &kernel.SyscallTable{ 183: syscalls.ErrorWithEvent("mq_timedreceive", syserror.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) 184: syscalls.ErrorWithEvent("mq_notify", syserror.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) 185: syscalls.ErrorWithEvent("mq_getsetattr", syserror.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) - 186: syscalls.ErrorWithEvent("msgget", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) - 187: syscalls.ErrorWithEvent("msgctl", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) - 188: syscalls.ErrorWithEvent("msgrcv", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) - 189: syscalls.ErrorWithEvent("msgsnd", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 186: syscalls.Supported("msgget", Msgget), + 187: syscalls.PartiallySupported("msgctl", Msgctl, "Only supports IPC_RMID option.", []string{"gvisor.dev/issue/135"}), + 188: syscalls.ErrorWithEvent("msgrcv", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 189: syscalls.ErrorWithEvent("msgsnd", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) 190: syscalls.Supported("semget", Semget), 191: syscalls.Supported("semctl", Semctl), 192: syscalls.Supported("semtimedop", Semtimedop), diff --git a/pkg/sentry/syscalls/linux/sys_msgqueue.go b/pkg/sentry/syscalls/linux/sys_msgqueue.go new file mode 100644 index 000000000..3476e218d --- /dev/null +++ b/pkg/sentry/syscalls/linux/sys_msgqueue.go @@ -0,0 +1,57 @@ +// Copyright 2021 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 linux + +import ( + "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/errors/linuxerr" + "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/sentry/kernel/ipc" +) + +// Msgget implements msgget(2). +func Msgget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + key := ipc.Key(args[0].Int()) + flag := args[1].Int() + + private := key == linux.IPC_PRIVATE + create := flag&linux.IPC_CREAT == linux.IPC_CREAT + exclusive := flag&linux.IPC_EXCL == linux.IPC_EXCL + mode := linux.FileMode(flag & 0777) + + r := t.IPCNamespace().MsgqueueRegistry() + queue, err := r.FindOrCreate(t, key, mode, private, create, exclusive) + if err != nil { + return 0, nil, err + } + return uintptr(queue.ID()), nil, nil +} + +// Msgctl implements msgctl(2). +func Msgctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + id := ipc.ID(args[0].Int()) + cmd := args[1].Int() + + creds := auth.CredentialsFromContext(t) + + switch cmd { + case linux.IPC_RMID: + return 0, nil, t.IPCNamespace().MsgqueueRegistry().Remove(id, creds) + default: + return 0, nil, linuxerr.EINVAL + } +} diff --git a/test/syscalls/linux/msgqueue.cc b/test/syscalls/linux/msgqueue.cc index ba61bba34..2409de7e8 100644 --- a/test/syscalls/linux/msgqueue.cc +++ b/test/syscalls/linux/msgqueue.cc @@ -48,9 +48,6 @@ class Queue { // Test simple creation and retrieval for msgget(2). TEST(MsgqueueTest, MsgGet) { - // Don't run test until syscall is implemented. - GTEST_SKIP(); - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); const key_t key = ftok(keyfile.path().c_str(), 1); ASSERT_THAT(key, SyscallSucceeds()); @@ -62,9 +59,6 @@ TEST(MsgqueueTest, MsgGet) { // Test simple failure scenarios for msgget(2). TEST(MsgqueueTest, MsgGetFail) { - // Don't run test until syscall is implemented. - GTEST_SKIP(); - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); const key_t key = ftok(keyfile.path().c_str(), 1); ASSERT_THAT(key, SyscallSucceeds()); @@ -79,9 +73,6 @@ TEST(MsgqueueTest, MsgGetFail) { // Test using msgget(2) with IPC_PRIVATE option. TEST(MsgqueueTest, MsgGetIpcPrivate) { - // Don't run test until syscall is implemented. - GTEST_SKIP(); - Queue queue1(msgget(IPC_PRIVATE, 0)); ASSERT_THAT(queue1.get(), SyscallSucceeds()); -- cgit v1.2.3