diff options
-rw-r--r-- | pkg/sentry/syscalls/linux/vfs2/BUILD | 2 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/vfs2/mq.go | 98 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/vfs2/vfs2.go | 5 | ||||
-rw-r--r-- | test/syscalls/linux/mq.cc | 22 |
4 files changed, 113 insertions, 14 deletions
diff --git a/pkg/sentry/syscalls/linux/vfs2/BUILD b/pkg/sentry/syscalls/linux/vfs2/BUILD index 1e3bd2a50..4a03008f8 100644 --- a/pkg/sentry/syscalls/linux/vfs2/BUILD +++ b/pkg/sentry/syscalls/linux/vfs2/BUILD @@ -19,6 +19,7 @@ go_library( "memfd.go", "mmap.go", "mount.go", + "mq.go", "path.go", "pipe.go", "poll.go", @@ -59,6 +60,7 @@ go_library( "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/fasync", + "//pkg/sentry/kernel/mq", "//pkg/sentry/kernel/pipe", "//pkg/sentry/kernel/time", "//pkg/sentry/limits", diff --git a/pkg/sentry/syscalls/linux/vfs2/mq.go b/pkg/sentry/syscalls/linux/vfs2/mq.go new file mode 100644 index 000000000..d5d81c6e2 --- /dev/null +++ b/pkg/sentry/syscalls/linux/vfs2/mq.go @@ -0,0 +1,98 @@ +// 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 vfs2 + +import ( + "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/pkg/sentry/kernel/mq" +) + +// MqOpen implements mq_open(2). +func MqOpen(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + nameAddr := args[0].Pointer() + flag := args[1].Int() + mode := args[2].ModeT() + attrAddr := args[3].Pointer() + + name, err := t.CopyInString(nameAddr, mq.MaxName) + if err != nil { + return 0, nil, err + } + + rOnly := flag&linux.O_RDONLY == linux.O_RDONLY + wOnly := flag&linux.O_WRONLY == linux.O_WRONLY + readWrite := flag&linux.O_RDWR == linux.O_RDWR + + create := flag&linux.O_CREAT == linux.O_CREAT + exclusive := flag&linux.O_EXCL == linux.O_EXCL + block := flag&linux.O_NONBLOCK != linux.O_NONBLOCK + + var attr linux.MqAttr + var attrPtr *linux.MqAttr + if attrAddr != 0 { + if _, err := attr.CopyIn(t, attrAddr); err != nil { + return 0, nil, err + } + attrPtr = &attr + } + + opts := openOpts(name, rOnly, wOnly, readWrite, create, exclusive, block) + + r := t.IPCNamespace().PosixQueues() + queue, err := r.FindOrCreate(t, opts, linux.FileMode(mode), attrPtr) + if err != nil { + return 0, nil, err + } + + fd, err := t.NewFDFromVFS2(0, queue, kernel.FDFlags{ + CloseOnExec: flag&linux.O_CLOEXEC != 0, + }) + if err != nil { + return 0, nil, err + } + return uintptr(fd), nil, nil +} + +// MqUnlink implements mq_unlink(2). +func MqUnlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + nameAddr := args[0].Pointer() + name, err := t.CopyInString(nameAddr, mq.MaxName) + if err != nil { + return 0, nil, err + } + return 0, nil, t.IPCNamespace().PosixQueues().Remove(t, name) +} + +func openOpts(name string, rOnly, wOnly, readWrite, create, exclusive, block bool) mq.OpenOpts { + var access mq.AccessType + switch { + case readWrite: + access = mq.ReadWrite + case wOnly: + access = mq.WriteOnly + case rOnly: + access = mq.ReadOnly + } + + return mq.OpenOpts{ + Name: name, + Access: access, + Create: create, + Exclusive: exclusive, + Block: block, + } +} diff --git a/pkg/sentry/syscalls/linux/vfs2/vfs2.go b/pkg/sentry/syscalls/linux/vfs2/vfs2.go index 0fc81e694..4eb15a7f2 100644 --- a/pkg/sentry/syscalls/linux/vfs2/vfs2.go +++ b/pkg/sentry/syscalls/linux/vfs2/vfs2.go @@ -112,6 +112,8 @@ func Override() { s.Table[232] = syscalls.Supported("epoll_wait", EpollWait) s.Table[233] = syscalls.Supported("epoll_ctl", EpollCtl) s.Table[235] = syscalls.Supported("utimes", Utimes) + s.Table[240] = syscalls.Supported("mq_open", MqOpen) + s.Table[241] = syscalls.Supported("mq_unlink", MqUnlink) s.Table[253] = syscalls.PartiallySupported("inotify_init", InotifyInit, "inotify events are only available inside the sandbox.", nil) s.Table[254] = syscalls.PartiallySupported("inotify_add_watch", InotifyAddWatch, "inotify events are only available inside the sandbox.", nil) s.Table[255] = syscalls.PartiallySupported("inotify_rm_watch", InotifyRmWatch, "inotify events are only available inside the sandbox.", nil) @@ -241,6 +243,8 @@ func Override() { s.Table[86] = syscalls.Supported("timerfd_settime", TimerfdSettime) s.Table[87] = syscalls.Supported("timerfd_gettime", TimerfdGettime) s.Table[88] = syscalls.Supported("utimensat", Utimensat) + s.Table[180] = syscalls.Supported("mq_open", MqOpen) + s.Table[181] = syscalls.Supported("mq_unlink", MqUnlink) s.Table[198] = syscalls.Supported("socket", Socket) s.Table[199] = syscalls.Supported("socketpair", SocketPair) s.Table[200] = syscalls.Supported("bind", Bind) @@ -271,6 +275,5 @@ func Override() { s.Table[287] = syscalls.Supported("pwritev2", Pwritev2) s.Table[291] = syscalls.Supported("statx", Statx) s.Table[441] = syscalls.Supported("epoll_pwait2", EpollPwait2) - s.Init() } diff --git a/test/syscalls/linux/mq.cc b/test/syscalls/linux/mq.cc index 9a4a87dfe..b3cd6d1b9 100644 --- a/test/syscalls/linux/mq.cc +++ b/test/syscalls/linux/mq.cc @@ -121,14 +121,14 @@ PosixError MqClose(mqd_t fd) { // Test simple opening and closing of a message queue. TEST(MqTest, Open) { - GTEST_SKIP(); + SKIP_IF(IsRunningWithVFS1()); EXPECT_THAT(MqOpen(O_RDWR | O_CREAT | O_EXCL, 0777, nullptr), IsPosixErrorOkAndHolds(_)); } // Test mq_open(2) after mq_unlink(2). TEST(MqTest, OpenAfterUnlink) { - GTEST_SKIP(); + SKIP_IF(IsRunningWithVFS1()); PosixQueue queue = ASSERT_NO_ERRNO_AND_VALUE( MqOpen(O_RDWR | O_CREAT | O_EXCL, 0777, nullptr)); @@ -140,7 +140,7 @@ TEST(MqTest, OpenAfterUnlink) { // Test using invalid args with mq_open. TEST(MqTest, OpenInvalidArgs) { - GTEST_SKIP(); + SKIP_IF(IsRunningWithVFS1()); // Name must start with a slash. EXPECT_THAT(MqOpen("test", O_RDWR), PosixErrorIs(EINVAL)); @@ -180,7 +180,7 @@ TEST(MqTest, OpenInvalidArgs) { // Test creating a queue that already exists. TEST(MqTest, CreateAlreadyExists) { - GTEST_SKIP(); + SKIP_IF(IsRunningWithVFS1()); PosixQueue queue = ASSERT_NO_ERRNO_AND_VALUE( MqOpen(O_RDWR | O_CREAT | O_EXCL, 0777, nullptr)); @@ -191,7 +191,7 @@ TEST(MqTest, CreateAlreadyExists) { // Test opening a queue that doesn't exists. TEST(MqTest, NoQueueExists) { - GTEST_SKIP(); + SKIP_IF(IsRunningWithVFS1()); // Choose a name to pass that's unlikely to exist if the test is run locally. EXPECT_THAT(MqOpen("/gvisor-mq-test-nonexistent-queue", O_RDWR), @@ -236,8 +236,8 @@ TEST(MqTest, OpenWriteAccess) { // Test changing IPC namespace. TEST(MqTest, ChangeIpcNamespace) { - GTEST_SKIP(); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + SKIP_IF(IsRunningWithVFS1() || + !ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); // When changing IPC namespaces, Linux doesn't invalidate or close the // previously opened file descriptions and allows operations to be performed @@ -267,7 +267,6 @@ TEST(MqTest, ChangeIpcNamespace) { // Test mounting the mqueue filesystem. TEST(MqTest, Mount) { - GTEST_SKIP(); SKIP_IF(IsRunningWithVFS1() || !ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); @@ -280,7 +279,6 @@ TEST(MqTest, Mount) { // Test mounting the mqueue filesystem to several places. TEST(MqTest, MountSeveral) { - GTEST_SKIP(); SKIP_IF(IsRunningWithVFS1() || !ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); @@ -299,7 +297,6 @@ TEST(MqTest, MountSeveral) { // Test mounting mqueue and opening a queue as normal file. TEST(MqTest, OpenAsFile) { - GTEST_SKIP(); SKIP_IF(IsRunningWithVFS1() || !ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); @@ -328,7 +325,6 @@ TEST(MqTest, OpenAsFile) { // Test removing a queue using unlink(2). TEST(MqTest, UnlinkAsFile) { - GTEST_SKIP(); SKIP_IF(IsRunningWithVFS1() || !ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); @@ -349,7 +345,7 @@ TEST(MqTest, UnlinkAsFile) { // Test read(2) from an empty queue. TEST(MqTest, ReadEmpty) { - GTEST_SKIP(); + SKIP_IF(IsRunningWithVFS1()); PosixQueue queue = ASSERT_NO_ERRNO_AND_VALUE( MqOpen(O_RDWR | O_CREAT | O_EXCL, 0777, nullptr)); @@ -368,7 +364,7 @@ TEST(MqTest, ReadEmpty) { // Test poll(2) on an empty queue. TEST(MqTest, PollEmpty) { - GTEST_SKIP(); + SKIP_IF(IsRunningWithVFS1()); PosixQueue queue = ASSERT_NO_ERRNO_AND_VALUE( MqOpen(O_RDWR | O_CREAT | O_EXCL, 0777, nullptr)); |