mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement stubs for msgsnd(2) and msgrcv(2).
Add support for msgsnd and msgrcv and enable syscall tests. Updates #135
This commit is contained in:
@@ -386,6 +386,15 @@ func (q *Queue) pop(ctx context.Context, creds *auth.Credentials, mType int64, m
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// Copy copies a message from the queue without deleting it. See
|
||||
// msgrcv(MSG_COPY).
|
||||
func (q *Queue) Copy() (*Message, error) {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
return nil, linuxerr.ENOSYS
|
||||
}
|
||||
|
||||
// msgOfType returns the first message with the specified type, nil if no
|
||||
// message is found. If except is true, the first message of a type not equal
|
||||
// to mType will be returned.
|
||||
|
||||
@@ -86,6 +86,7 @@ go_library(
|
||||
"//pkg/sentry/kernel/eventfd",
|
||||
"//pkg/sentry/kernel/fasync",
|
||||
"//pkg/sentry/kernel/ipc",
|
||||
"//pkg/sentry/kernel/msgqueue",
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
"//pkg/sentry/kernel/sched",
|
||||
"//pkg/sentry/kernel/shm",
|
||||
|
||||
@@ -122,8 +122,8 @@ var AMD64 = &kernel.SyscallTable{
|
||||
66: syscalls.Supported("semctl", Semctl),
|
||||
67: syscalls.Supported("shmdt", Shmdt),
|
||||
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)
|
||||
69: syscalls.Supported("msgsnd", Msgsnd),
|
||||
70: syscalls.PartiallySupported("msgrcv", Msgrcv, "Doesn't support MSG_COPY option.", []string{"gvisor.dev/issue/135"}),
|
||||
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),
|
||||
@@ -618,8 +618,8 @@ var ARM64 = &kernel.SyscallTable{
|
||||
185: syscalls.ErrorWithEvent("mq_getsetattr", syserror.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // 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)
|
||||
188: syscalls.PartiallySupported("msgrcv", Msgrcv, "Doesn't support MSG_COPY option.", []string{"gvisor.dev/issue/135"}),
|
||||
189: syscalls.Supported("msgsnd", Msgsnd),
|
||||
190: syscalls.Supported("semget", Semget),
|
||||
191: syscalls.Supported("semctl", Semctl),
|
||||
192: syscalls.Supported("semtimedop", Semtimedop),
|
||||
|
||||
@@ -17,10 +17,12 @@ package linux
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"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"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/msgqueue"
|
||||
)
|
||||
|
||||
// Msgget implements msgget(2).
|
||||
@@ -41,6 +43,86 @@ func Msgget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
return uintptr(queue.ID()), nil, nil
|
||||
}
|
||||
|
||||
// Msgsnd implements msgsnd(2).
|
||||
func Msgsnd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
id := ipc.ID(args[0].Int())
|
||||
msgAddr := args[1].Pointer()
|
||||
size := args[2].Int64()
|
||||
flag := args[3].Int()
|
||||
|
||||
if size < 0 || size > linux.MSGMAX {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
wait := flag&linux.IPC_NOWAIT != linux.IPC_NOWAIT
|
||||
pid := int32(t.ThreadGroup().ID())
|
||||
|
||||
buf := linux.MsgBuf{
|
||||
Text: make([]byte, size),
|
||||
}
|
||||
if _, err := buf.CopyIn(t, msgAddr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
queue, err := t.IPCNamespace().MsgqueueRegistry().FindByID(id)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
msg := msgqueue.Message{
|
||||
Type: int64(buf.Type),
|
||||
Text: buf.Text,
|
||||
Size: uint64(size),
|
||||
}
|
||||
return 0, nil, queue.Send(t, msg, t, wait, pid)
|
||||
}
|
||||
|
||||
// Msgrcv implements msgrcv(2).
|
||||
func Msgrcv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
id := ipc.ID(args[0].Int())
|
||||
msgAddr := args[1].Pointer()
|
||||
size := args[2].Int64()
|
||||
mType := args[3].Int64()
|
||||
flag := args[4].Int()
|
||||
|
||||
wait := flag&linux.IPC_NOWAIT != linux.IPC_NOWAIT
|
||||
except := flag&linux.MSG_EXCEPT == linux.MSG_EXCEPT
|
||||
truncate := flag&linux.MSG_NOERROR == linux.MSG_NOERROR
|
||||
|
||||
msgCopy := flag&linux.MSG_COPY == linux.MSG_COPY
|
||||
|
||||
msg, err := receive(t, id, mType, size, msgCopy, wait, truncate, except)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
buf := linux.MsgBuf{
|
||||
Type: primitive.Int64(msg.Type),
|
||||
Text: msg.Text,
|
||||
}
|
||||
if _, err := buf.CopyOut(t, msgAddr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(msg.Size), nil, nil
|
||||
}
|
||||
|
||||
// receive returns a message from the queue with the given ID. If msgCopy is
|
||||
// true, a message is copied from the queue without being removed. Otherwise,
|
||||
// a message is removed from the queue and returned.
|
||||
func receive(t *kernel.Task, id ipc.ID, mType int64, maxSize int64, msgCopy, wait, truncate, except bool) (*msgqueue.Message, error) {
|
||||
pid := int32(t.ThreadGroup().ID())
|
||||
|
||||
queue, err := t.IPCNamespace().MsgqueueRegistry().FindByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if msgCopy {
|
||||
return queue.Copy()
|
||||
}
|
||||
return queue.Receive(t, t, mType, maxSize, wait, truncate, except, pid)
|
||||
}
|
||||
|
||||
// Msgctl implements msgctl(2).
|
||||
func Msgctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
id := ipc.ID(args[0].Int())
|
||||
|
||||
@@ -26,11 +26,6 @@ namespace gvisor {
|
||||
namespace testing {
|
||||
namespace {
|
||||
|
||||
// run is a temporary variable to easily enable/disable running tests. This
|
||||
// variable should be removed along with SKIP_IF when the tested functionality
|
||||
// is enabled.
|
||||
constexpr bool run = false;
|
||||
|
||||
constexpr int msgMax = 8192; // Max size for message in bytes.
|
||||
constexpr int msgMni = 32000; // Max number of identifiers.
|
||||
constexpr int msgMnb = 16384; // Default max size of message queue in bytes.
|
||||
@@ -115,8 +110,6 @@ TEST(MsgqueueTest, MsgGetIpcPrivate) {
|
||||
|
||||
// Test simple msgsnd and msgrcv.
|
||||
TEST(MsgqueueTest, MsgOpSimple) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -133,8 +126,6 @@ TEST(MsgqueueTest, MsgOpSimple) {
|
||||
|
||||
// Test msgsnd and msgrcv of an empty message.
|
||||
TEST(MsgqueueTest, MsgOpEmpty) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -148,8 +139,6 @@ TEST(MsgqueueTest, MsgOpEmpty) {
|
||||
|
||||
// Test truncation of message with MSG_NOERROR flag.
|
||||
TEST(MsgqueueTest, MsgOpTruncate) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -165,8 +154,6 @@ TEST(MsgqueueTest, MsgOpTruncate) {
|
||||
|
||||
// Test msgsnd and msgrcv using invalid arguments.
|
||||
TEST(MsgqueueTest, MsgOpInvalidArgs) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -185,12 +172,10 @@ TEST(MsgqueueTest, MsgOpInvalidArgs) {
|
||||
|
||||
// Test non-blocking msgrcv with an empty queue.
|
||||
TEST(MsgqueueTest, MsgOpNoMsg) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
msgbuf rcv{1, ""};
|
||||
msgbuf rcv;
|
||||
EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(rcv.mtext) + 1, 0, IPC_NOWAIT),
|
||||
SyscallFailsWithErrno(ENOMSG));
|
||||
}
|
||||
@@ -198,8 +183,6 @@ TEST(MsgqueueTest, MsgOpNoMsg) {
|
||||
// Test non-blocking msgrcv with a non-empty queue, but no messages of wanted
|
||||
// type.
|
||||
TEST(MsgqueueTest, MsgOpNoMsgType) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -213,8 +196,6 @@ TEST(MsgqueueTest, MsgOpNoMsgType) {
|
||||
|
||||
// Test msgrcv with a larger size message than wanted, and truncation disabled.
|
||||
TEST(MsgqueueTest, MsgOpTooBig) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -228,8 +209,6 @@ TEST(MsgqueueTest, MsgOpTooBig) {
|
||||
|
||||
// Test receiving messages based on type.
|
||||
TEST(MsgqueueTest, MsgRcvType) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -257,8 +236,6 @@ TEST(MsgqueueTest, MsgRcvType) {
|
||||
|
||||
// Test using MSG_EXCEPT to receive a different-type message.
|
||||
TEST(MsgqueueTest, MsgExcept) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -285,8 +262,6 @@ TEST(MsgqueueTest, MsgExcept) {
|
||||
|
||||
// Test msgrcv with a negative type.
|
||||
TEST(MsgqueueTest, MsgRcvTypeNegative) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -309,8 +284,6 @@ TEST(MsgqueueTest, MsgRcvTypeNegative) {
|
||||
|
||||
// Test permission-related failure scenarios.
|
||||
TEST(MsgqueueTest, MsgOpPermissions) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
AutoCapability cap(CAP_IPC_OWNER, false);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0000));
|
||||
@@ -326,8 +299,6 @@ TEST(MsgqueueTest, MsgOpPermissions) {
|
||||
|
||||
// Test limits for messages and queues.
|
||||
TEST(MsgqueueTest, MsgOpLimits) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -376,8 +347,6 @@ bool MsgCopySupported() {
|
||||
|
||||
// Test usage of MSG_COPY for msgrcv.
|
||||
TEST(MsgqueueTest, MsgCopy) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
SKIP_IF(!MsgCopySupported());
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
@@ -419,8 +388,6 @@ TEST(MsgqueueTest, MsgCopy) {
|
||||
|
||||
// Test msgrcv (most probably) blocking on an empty queue.
|
||||
TEST(MsgqueueTest, MsgRcvBlocking) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -449,8 +416,6 @@ TEST(MsgqueueTest, MsgRcvBlocking) {
|
||||
|
||||
// Test msgrcv (most probably) waiting for a specific-type message.
|
||||
TEST(MsgqueueTest, MsgRcvTypeBlocking) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -487,8 +452,6 @@ TEST(MsgqueueTest, MsgRcvTypeBlocking) {
|
||||
|
||||
// Test msgsnd (most probably) blocking on a full queue.
|
||||
TEST(MsgqueueTest, MsgSndBlocking) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -508,8 +471,7 @@ TEST(MsgqueueTest, MsgSndBlocking) {
|
||||
if (child_pid == 0) {
|
||||
// Fill the queue.
|
||||
for (size_t i = 0; i < msgCount; i++) {
|
||||
EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
|
||||
SyscallSucceeds());
|
||||
TEST_PCHECK(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0) == 0);
|
||||
}
|
||||
|
||||
// Next msgsnd should block.
|
||||
@@ -531,7 +493,7 @@ TEST(MsgqueueTest, MsgSndBlocking) {
|
||||
// Delay a bit more for the blocking msgsnd.
|
||||
absl::SleepFor(absl::Milliseconds(100));
|
||||
|
||||
EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) + 1, 0, 0),
|
||||
EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext), 0, 0),
|
||||
SyscallSucceedsWithValue(sizeof(buf.mtext)));
|
||||
|
||||
int status;
|
||||
@@ -542,8 +504,6 @@ TEST(MsgqueueTest, MsgSndBlocking) {
|
||||
|
||||
// Test removing a queue while a blocking msgsnd is executing.
|
||||
TEST(MsgqueueTest, MsgSndRmWhileBlocking) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -592,8 +552,6 @@ TEST(MsgqueueTest, MsgSndRmWhileBlocking) {
|
||||
|
||||
// Test removing a queue while a blocking msgrcv is executing.
|
||||
TEST(MsgqueueTest, MsgRcvRmWhileBlocking) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
@@ -620,8 +578,6 @@ TEST(MsgqueueTest, MsgRcvRmWhileBlocking) {
|
||||
|
||||
// Test a collection of msgsnd/msgrcv operations in different processes.
|
||||
TEST(MsgqueueTest, MsgOpGeneral) {
|
||||
SKIP_IF(!run);
|
||||
|
||||
Queue queue(msgget(IPC_PRIVATE, 0600));
|
||||
ASSERT_THAT(queue.get(), SyscallSucceeds());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user