From 122fd928f9e812e968d3fd6814da19475413aba6 Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Mon, 28 Jun 2021 22:04:20 +0200 Subject: Add tests for msgctl(2). Updates #135 --- test/syscalls/linux/msgqueue.cc | 192 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 2 deletions(-) diff --git a/test/syscalls/linux/msgqueue.cc b/test/syscalls/linux/msgqueue.cc index 837e913d9..e0fb63adf 100644 --- a/test/syscalls/linux/msgqueue.cc +++ b/test/syscalls/linux/msgqueue.cc @@ -26,9 +26,15 @@ namespace gvisor { namespace testing { namespace { -constexpr int msgMax = 8192; // Max size for message in bytes. +// Source: include/uapi/linux/msg.h +constexpr int msgMnb = 16384; // Maximum number of bytes in a queue. constexpr int msgMni = 32000; // Max number of identifiers. -constexpr int msgMnb = 16384; // Default max size of message queue in bytes. +constexpr int msgPool = + (msgMni * msgMnb / 1024); // Size of buffer pool used to hold message data. +constexpr int msgMap = msgMnb; // Maximum number of entries in message map. +constexpr int msgMax = 8192; // Maximum number of bytes in a single message. +constexpr int msgSsz = 16; // Message segment size. +constexpr int msgTql = msgMnb; // Maximum number of messages on all queues. // Queue is a RAII class used to automatically clean message queues. class Queue { @@ -654,6 +660,188 @@ TEST(MsgqueueTest, MsgOpGeneral) { } } +// Test msgctl with IPC_STAT option. +TEST(MsgqueueTest, MsgCtlIpcStat) { + GTEST_SKIP(); + + auto start = absl::Now(); + + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + const uid_t uid = getuid(); + const gid_t gid = getgid(); + const pid_t pid = getpid(); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + + EXPECT_EQ(ds.msg_perm.__key, IPC_PRIVATE); + EXPECT_EQ(ds.msg_perm.uid, uid); + EXPECT_EQ(ds.msg_perm.gid, gid); + EXPECT_EQ(ds.msg_perm.cuid, uid); + EXPECT_EQ(ds.msg_perm.cgid, gid); + EXPECT_EQ(ds.msg_perm.mode, 0600); + + EXPECT_EQ(ds.msg_stime, 0); + EXPECT_EQ(ds.msg_rtime, 0); + EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start)); + + EXPECT_EQ(ds.msg_cbytes, 0); + EXPECT_EQ(ds.msg_qnum, 0); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + EXPECT_EQ(ds.msg_lspid, 0); + EXPECT_EQ(ds.msg_lrpid, 0); + + // The timestamps only have a resolution of seconds; slow down so we actually + // see the timestamps change. + absl::SleepFor(absl::Seconds(1)); + auto pre_send = absl::Now(); + + msgbuf buf; + ASSERT_THAT(msgsnd(queue.get(), &buf, msgSize, 0), SyscallSucceeds()); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + + EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send)); + EXPECT_EQ(ds.msg_rtime, 0); + EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start)); + + EXPECT_EQ(ds.msg_cbytes, msgSize); + EXPECT_EQ(ds.msg_qnum, 1); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + EXPECT_EQ(ds.msg_lspid, pid); + EXPECT_EQ(ds.msg_lrpid, 0); + + absl::SleepFor(absl::Seconds(1)); + auto pre_receive = absl::Now(); + + ASSERT_THAT(msgrcv(queue.get(), &buf, msgSize, 0, 0), + SyscallSucceedsWithValue(msgSize)); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + + EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send)); + EXPECT_GE(ds.msg_rtime, absl::ToTimeT(pre_receive)); + EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start)); + + EXPECT_EQ(ds.msg_cbytes, 0); + EXPECT_EQ(ds.msg_qnum, 0); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + EXPECT_EQ(ds.msg_lspid, pid); + EXPECT_EQ(ds.msg_lrpid, pid); +} + +// Test msgctl with IPC_STAT option on a write-only queue. +TEST(MsgqueueTest, MsgCtlIpcStatWriteOnly) { + GTEST_SKIP(); + + // Drop CAP_IPC_OWNER which allows us to bypass permissions. + AutoCapability cap(CAP_IPC_OWNER, false); + + Queue queue(msgget(IPC_PRIVATE, 0200)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), + SyscallFailsWithErrno(EACCES)); +} + +// Test msgctl with IPC_SET option. +TEST(MsgqueueTest, MsgCtlIpcSet) { + GTEST_SKIP(); + + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_perm.mode, 0600); + + ds.msg_perm.mode = 0777; + ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds()); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_perm.mode, 0777); +} + +// Test increasing msg_qbytes beyond limit with IPC_SET. +TEST(MsgqueueTest, MsgCtlIpcSetMaxBytes) { + GTEST_SKIP(); + + // Drop CAP_SYS_RESOURCE which allows us to increase msg_qbytes beyond the + // system parameter MSGMNB. + AutoCapability cap(CAP_SYS_RESOURCE, false); + + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + + ds.msg_qbytes = msgMnb - 10; + ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds()); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_qbytes, msgMnb - 10); + + ds.msg_qbytes = msgMnb + 10; + EXPECT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallFailsWithErrno(EPERM)); +} + +// Test msgctl with IPC_INFO option. +TEST(MsgqueueTest, MsgCtlIpcInfo) { + GTEST_SKIP(); + + struct msginfo info; + ASSERT_THAT(msgctl(0, IPC_INFO, reinterpret_cast(&info)), + SyscallSucceeds()); + + EXPECT_GT(info.msgmax, 0); + EXPECT_GT(info.msgmni, 0); + EXPECT_GT(info.msgmnb, 0); + EXPECT_EQ(info.msgpool, msgPool); + EXPECT_EQ(info.msgmap, msgMap); + EXPECT_EQ(info.msgssz, msgSsz); + EXPECT_EQ(info.msgtql, msgTql); +} + +// Test msgctl with MSG_INFO option. +TEST(MsgqueueTest, MsgCtlMsgInfo) { + GTEST_SKIP(); + + struct msginfo info; + ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast(&info)), + SyscallSucceeds()); + + EXPECT_GT(info.msgmax, 0); + EXPECT_GT(info.msgmni, 0); + EXPECT_GT(info.msgmnb, 0); + EXPECT_EQ(info.msgpool, 0); // Number of queues in the system. + EXPECT_EQ(info.msgmap, 0); // Total number of messages in all queues. + EXPECT_EQ(info.msgtql, 0); // Total number of bytes in all messages. + EXPECT_EQ(info.msgssz, msgSsz); + + // Add a queue and a message. + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + msgbuf buf; + ASSERT_THAT(msgsnd(queue.get(), &buf, msgSize, 0), SyscallSucceeds()); + + ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast(&info)), + SyscallSucceeds()); + + EXPECT_GT(info.msgmax, 0); + EXPECT_GT(info.msgmni, 0); + EXPECT_GT(info.msgmnb, 0); + EXPECT_EQ(info.msgpool, 1); // Number of queues in the system. + EXPECT_EQ(info.msgmap, 1); // Total number of messages in all queues. + EXPECT_EQ(info.msgtql, msgSize); // Total number of bytes in all messages. + EXPECT_EQ(info.msgssz, msgSsz); +} + } // namespace } // namespace testing } // namespace gvisor -- cgit v1.2.3 From 2cf61eab4a4ead7cbbec1d06d71e9406f5983251 Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Tue, 29 Jun 2021 23:25:51 +0200 Subject: Implement ipc.Object.Set and use it in ipc mechanisms. Set provides functionality of {sem,shm,msg}ctl(IPC_SET). --- pkg/sentry/kernel/ipc/object.go | 35 ++++++++++++++++++++++++++++++++ pkg/sentry/kernel/semaphore/semaphore.go | 12 ++++------- pkg/sentry/kernel/shm/shm.go | 19 ++--------------- pkg/sentry/syscalls/linux/sys_sem.go | 19 +++-------------- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/pkg/sentry/kernel/ipc/object.go b/pkg/sentry/kernel/ipc/object.go index 387b35e7e..facd157c7 100644 --- a/pkg/sentry/kernel/ipc/object.go +++ b/pkg/sentry/kernel/ipc/object.go @@ -19,6 +19,8 @@ package ipc 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" ) @@ -113,3 +115,36 @@ func (o *Object) CheckPermissions(creds *auth.Credentials, req fs.PermMask) bool } return creds.HasCapabilityIn(linux.CAP_IPC_OWNER, o.UserNS) } + +// Set modifies attributes for an IPC object. See *ctl(IPC_SET). +// +// Precondition: Mechanism.mu must be held. +func (o *Object) Set(ctx context.Context, perm *linux.IPCPerm) error { + creds := auth.CredentialsFromContext(ctx) + uid := creds.UserNamespace.MapToKUID(auth.UID(perm.UID)) + gid := creds.UserNamespace.MapToKGID(auth.GID(perm.GID)) + if !uid.Ok() || !gid.Ok() { + // The man pages don't specify an errno for invalid uid/gid, but EINVAL + // is generally used for invalid arguments. + return linuxerr.EINVAL + } + + if !o.CheckOwnership(creds) { + // "The argument cmd has the value IPC_SET or IPC_RMID, but the + // effective user ID of the calling process is not the creator (as + // found in msg_perm.cuid) or the owner (as found in msg_perm.uid) + // of the message queue, and the caller is not privileged (Linux: + // does not have the CAP_SYS_ADMIN capability)." + return linuxerr.EPERM + } + + // User may only modify the lower 9 bits of the mode. All the other bits are + // always 0 for the underlying inode. + mode := linux.FileMode(perm.Mode & 0x1ff) + + o.Perms = fs.FilePermsFromMode(mode) + o.Owner.UID = uid + o.Owner.GID = gid + + return nil +} diff --git a/pkg/sentry/kernel/semaphore/semaphore.go b/pkg/sentry/kernel/semaphore/semaphore.go index 8610d3fc1..52030df19 100644 --- a/pkg/sentry/kernel/semaphore/semaphore.go +++ b/pkg/sentry/kernel/semaphore/semaphore.go @@ -337,19 +337,15 @@ func (s *Set) Size() int { return len(s.sems) } -// Change changes some fields from the set atomically. -func (s *Set) Change(ctx context.Context, creds *auth.Credentials, owner fs.FileOwner, perms fs.FilePermissions) error { +// Set modifies attributes for a semaphore set. See semctl(IPC_SET). +func (s *Set) Set(ctx context.Context, ds *linux.SemidDS) error { s.mu.Lock() defer s.mu.Unlock() - // "The effective UID of the calling process must match the owner or creator - // of the semaphore set, or the caller must be privileged." - if !s.obj.CheckOwnership(creds) { - return linuxerr.EACCES + if err := s.obj.Set(ctx, &ds.SemPerm); err != nil { + return err } - s.obj.Owner = owner - s.obj.Perms = perms s.changeTime = ktime.NowFromContext(ctx) return nil } diff --git a/pkg/sentry/kernel/shm/shm.go b/pkg/sentry/kernel/shm/shm.go index 2abf467d7..ba0fbcf90 100644 --- a/pkg/sentry/kernel/shm/shm.go +++ b/pkg/sentry/kernel/shm/shm.go @@ -619,25 +619,10 @@ func (s *Shm) Set(ctx context.Context, ds *linux.ShmidDS) error { s.mu.Lock() defer s.mu.Unlock() - creds := auth.CredentialsFromContext(ctx) - if !s.obj.CheckOwnership(creds) { - return linuxerr.EPERM - } - - uid := creds.UserNamespace.MapToKUID(auth.UID(ds.ShmPerm.UID)) - gid := creds.UserNamespace.MapToKGID(auth.GID(ds.ShmPerm.GID)) - if !uid.Ok() || !gid.Ok() { - return linuxerr.EINVAL + if err := s.obj.Set(ctx, &ds.ShmPerm); err != nil { + return err } - // User may only modify the lower 9 bits of the mode. All the other bits are - // always 0 for the underlying inode. - mode := linux.FileMode(ds.ShmPerm.Mode & 0x1ff) - s.obj.Perms = fs.FilePermsFromMode(mode) - - s.obj.Owner.UID = uid - s.obj.Owner.GID = gid - s.changeTime = ktime.NowFromContext(ctx) return nil } diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go index f61cc466c..5a119b21c 100644 --- a/pkg/sentry/syscalls/linux/sys_sem.go +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -23,7 +23,6 @@ import ( "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/kernel/ipc" @@ -166,8 +165,7 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal return 0, nil, err } - perms := fs.FilePermsFromMode(linux.FileMode(s.SemPerm.Mode & 0777)) - return 0, nil, ipcSet(t, id, auth.UID(s.SemPerm.UID), auth.GID(s.SemPerm.GID), perms) + return 0, nil, ipcSet(t, id, &s) case linux.GETPID: v, err := getPID(t, id, num) @@ -243,24 +241,13 @@ func remove(t *kernel.Task, id ipc.ID) error { return r.Remove(id, creds) } -func ipcSet(t *kernel.Task, id ipc.ID, uid auth.UID, gid auth.GID, perms fs.FilePermissions) error { +func ipcSet(t *kernel.Task, id ipc.ID, ds *linux.SemidDS) error { r := t.IPCNamespace().SemaphoreRegistry() set := r.FindByID(id) if set == nil { return linuxerr.EINVAL } - - creds := auth.CredentialsFromContext(t) - kuid := creds.UserNamespace.MapToKUID(uid) - if !kuid.Ok() { - return linuxerr.EINVAL - } - kgid := creds.UserNamespace.MapToKGID(gid) - if !kgid.Ok() { - return linuxerr.EINVAL - } - owner := fs.FileOwner{UID: kuid, GID: kgid} - return set.Change(t, creds, owner, perms) + return set.Set(t, ds) } func ipcStat(t *kernel.Task, id ipc.ID) (*linux.SemidDS, error) { -- cgit v1.2.3 From 265deee8cbce8f7d517bf749eb131fbed5d88151 Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Tue, 29 Jun 2021 23:30:58 +0200 Subject: Implement control operations on msgqueue. For IPCInfo, update value of MSGSEG constant in abi to avoid overflow in MsgInfo.MsgSeg. MSGSEG was originaly simplified in abi, and is unused (by us and within the kernel), so updating it is okay. Updates #135 --- pkg/abi/linux/msgqueue.go | 2 +- pkg/sentry/kernel/msgqueue/msgqueue.go | 109 +++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/pkg/abi/linux/msgqueue.go b/pkg/abi/linux/msgqueue.go index e1e8d0357..0612a8214 100644 --- a/pkg/abi/linux/msgqueue.go +++ b/pkg/abi/linux/msgqueue.go @@ -47,7 +47,7 @@ const ( MSGSSZ = 16 // MSGSEG is simplified due to the inexistance of a ternary operator. - MSGSEG = (MSGPOOL * 1024) / MSGSSZ + MSGSEG = 0xffff ) // MsqidDS is equivelant to struct msqid64_ds. Source: diff --git a/pkg/sentry/kernel/msgqueue/msgqueue.go b/pkg/sentry/kernel/msgqueue/msgqueue.go index c111297d7..950edfd8c 100644 --- a/pkg/sentry/kernel/msgqueue/msgqueue.go +++ b/pkg/sentry/kernel/msgqueue/msgqueue.go @@ -206,6 +206,48 @@ func (r *Registry) FindByID(id ipc.ID) (*Queue, error) { return mech.(*Queue), nil } +// IPCInfo reports global parameters for message queues. See msgctl(IPC_INFO). +func (r *Registry) IPCInfo(ctx context.Context) *linux.MsgInfo { + return &linux.MsgInfo{ + MsgPool: linux.MSGPOOL, + MsgMap: linux.MSGMAP, + MsgMax: linux.MSGMAX, + MsgMnb: linux.MSGMNB, + MsgMni: linux.MSGMNI, + MsgSsz: linux.MSGSSZ, + MsgTql: linux.MSGTQL, + MsgSeg: linux.MSGSEG, + } +} + +// MsgInfo reports global parameters for message queues. See msgctl(MSG_INFO). +func (r *Registry) MsgInfo(ctx context.Context) *linux.MsgInfo { + r.mu.Lock() + defer r.mu.Unlock() + + var messages, bytes uint64 + r.reg.ForAllObjects( + func(o ipc.Mechanism) { + q := o.(*Queue) + q.mu.Lock() + messages += q.messageCount + bytes += q.byteCount + q.mu.Unlock() + }, + ) + + return &linux.MsgInfo{ + MsgPool: int32(r.reg.ObjectCount()), + MsgMap: int32(messages), + MsgTql: int32(bytes), + MsgMax: linux.MSGMAX, + MsgMnb: linux.MSGMNB, + MsgMni: linux.MSGMNI, + MsgSsz: linux.MSGSSZ, + MsgSeg: linux.MSGSEG, + } +} + // Send appends a message to the message queue, and returns an error if sending // fails. See msgsnd(2). func (q *Queue) Send(ctx context.Context, m Message, b Blocker, wait bool, pid int32) (err error) { @@ -452,6 +494,73 @@ func (q *Queue) msgAtIndex(mType int64) *Message { return msg } +// Set modifies some values of the queue. See msgctl(IPC_SET). +func (q *Queue) Set(ctx context.Context, ds *linux.MsqidDS) error { + q.mu.Lock() + defer q.mu.Unlock() + + creds := auth.CredentialsFromContext(ctx) + if ds.MsgQbytes > maxQueueBytes && !creds.HasCapabilityIn(linux.CAP_SYS_RESOURCE, q.obj.UserNS) { + // "An attempt (IPC_SET) was made to increase msg_qbytes beyond the + // system parameter MSGMNB, but the caller is not privileged (Linux: + // does not have the CAP_SYS_RESOURCE capability)." + return linuxerr.EPERM + } + + if err := q.obj.Set(ctx, &ds.MsgPerm); err != nil { + return err + } + + q.maxBytes = ds.MsgQbytes + q.changeTime = ktime.NowFromContext(ctx) + return nil +} + +// Stat returns a MsqidDS object filled with information about the queue. See +// msgctl(IPC_STAT) and msgctl(MSG_STAT). +func (q *Queue) Stat(ctx context.Context) (*linux.MsqidDS, error) { + return q.stat(ctx, fs.PermMask{Read: true}) +} + +// StatAny is similar to Queue.Stat, but doesn't require read permission. See +// msgctl(MSG_STAT_ANY). +func (q *Queue) StatAny(ctx context.Context) (*linux.MsqidDS, error) { + return q.stat(ctx, fs.PermMask{}) +} + +// stat returns a MsqidDS object filled with information about the queue. An +// error is returned if the user doesn't have the specified permissions. +func (q *Queue) stat(ctx context.Context, mask fs.PermMask) (*linux.MsqidDS, error) { + q.mu.Lock() + defer q.mu.Unlock() + + creds := auth.CredentialsFromContext(ctx) + if !q.obj.CheckPermissions(creds, mask) { + // "The caller must have read permission on the message queue." + return nil, linuxerr.EACCES + } + + return &linux.MsqidDS{ + MsgPerm: linux.IPCPerm{ + Key: uint32(q.obj.Key), + UID: uint32(creds.UserNamespace.MapFromKUID(q.obj.Owner.UID)), + GID: uint32(creds.UserNamespace.MapFromKGID(q.obj.Owner.GID)), + CUID: uint32(creds.UserNamespace.MapFromKUID(q.obj.Creator.UID)), + CGID: uint32(creds.UserNamespace.MapFromKGID(q.obj.Creator.GID)), + Mode: uint16(q.obj.Perms.LinuxMode()), + Seq: 0, // IPC sequences not supported. + }, + MsgStime: q.sendTime.TimeT(), + MsgRtime: q.receiveTime.TimeT(), + MsgCtime: q.changeTime.TimeT(), + MsgCbytes: q.byteCount, + MsgQnum: q.messageCount, + MsgQbytes: q.maxBytes, + MsgLspid: q.sendPID, + MsgLrpid: q.receivePID, + }, nil +} + // Lock implements ipc.Mechanism.Lock. func (q *Queue) Lock() { q.mu.Lock() -- cgit v1.2.3 From 2f1c65e7fa49ddf6debb5d8ba4a5b08e17404e2a Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Tue, 29 Jun 2021 23:53:25 +0200 Subject: Implement stub for msgctl(2). Add support for msgctl and enable tests. Fixes #135 --- pkg/sentry/syscalls/linux/linux64.go | 4 +-- pkg/sentry/syscalls/linux/sys_msgqueue.go | 53 ++++++++++++++++++++++++++++++- test/syscalls/linux/msgqueue.cc | 12 ------- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index 1ead3c7e8..a416dfb7d 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -124,7 +124,7 @@ var AMD64 = &kernel.SyscallTable{ 68: syscalls.Supported("msgget", Msgget), 69: syscalls.Supported("msgsnd", Msgsnd), 70: syscalls.Supported("msgrcv", Msgrcv), - 71: syscalls.PartiallySupported("msgctl", Msgctl, "Only supports IPC_RMID option.", []string{"gvisor.dev/issue/135"}), + 71: syscalls.Supported("msgctl", Msgctl), 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), @@ -617,7 +617,7 @@ var ARM64 = &kernel.SyscallTable{ 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.Supported("msgget", Msgget), - 187: syscalls.PartiallySupported("msgctl", Msgctl, "Only supports IPC_RMID option.", []string{"gvisor.dev/issue/135"}), + 187: syscalls.Supported("msgctl", Msgctl), 188: syscalls.Supported("msgrcv", Msgrcv), 189: syscalls.Supported("msgsnd", Msgsnd), 190: syscalls.Supported("semget", Semget), diff --git a/pkg/sentry/syscalls/linux/sys_msgqueue.go b/pkg/sentry/syscalls/linux/sys_msgqueue.go index 5259ade90..60b989ee7 100644 --- a/pkg/sentry/syscalls/linux/sys_msgqueue.go +++ b/pkg/sentry/syscalls/linux/sys_msgqueue.go @@ -130,12 +130,63 @@ func receive(t *kernel.Task, id ipc.ID, mType int64, maxSize int64, msgCopy, wai func Msgctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) cmd := args[1].Int() + buf := args[2].Pointer() creds := auth.CredentialsFromContext(t) + r := t.IPCNamespace().MsgqueueRegistry() + switch cmd { + case linux.IPC_INFO: + info := r.IPCInfo(t) + _, err := info.CopyOut(t, buf) + return 0, nil, err + case linux.MSG_INFO: + msgInfo := r.MsgInfo(t) + _, err := msgInfo.CopyOut(t, buf) + return 0, nil, err case linux.IPC_RMID: - return 0, nil, t.IPCNamespace().MsgqueueRegistry().Remove(id, creds) + return 0, nil, r.Remove(id, creds) + } + + // Remaining commands use a queue. + queue, err := r.FindByID(id) + if err != nil { + return 0, nil, err + } + + switch cmd { + case linux.MSG_STAT: + // Technically, we should be treating id as "an index into the kernel's + // internal array that maintains information about all shared memory + // segments on the system". Since we don't track segments in an array, + // we'll just pretend the msqid is the index and do the same thing as + // IPC_STAT. Linux also uses the index as the msqid. + fallthrough + case linux.IPC_STAT: + stat, err := queue.Stat(t) + if err != nil { + return 0, nil, err + } + _, err = stat.CopyOut(t, buf) + return 0, nil, err + + case linux.MSG_STAT_ANY: + stat, err := queue.StatAny(t) + if err != nil { + return 0, nil, err + } + _, err = stat.CopyOut(t, buf) + return 0, nil, err + + case linux.IPC_SET: + var ds linux.MsqidDS + if _, err := ds.CopyIn(t, buf); err != nil { + return 0, nil, linuxerr.EINVAL + } + err := queue.Set(t, &ds) + return 0, nil, err + default: return 0, nil, linuxerr.EINVAL } diff --git a/test/syscalls/linux/msgqueue.cc b/test/syscalls/linux/msgqueue.cc index e0fb63adf..6804478fd 100644 --- a/test/syscalls/linux/msgqueue.cc +++ b/test/syscalls/linux/msgqueue.cc @@ -662,8 +662,6 @@ TEST(MsgqueueTest, MsgOpGeneral) { // Test msgctl with IPC_STAT option. TEST(MsgqueueTest, MsgCtlIpcStat) { - GTEST_SKIP(); - auto start = absl::Now(); Queue queue(msgget(IPC_PRIVATE, 0600)); @@ -734,8 +732,6 @@ TEST(MsgqueueTest, MsgCtlIpcStat) { // Test msgctl with IPC_STAT option on a write-only queue. TEST(MsgqueueTest, MsgCtlIpcStatWriteOnly) { - GTEST_SKIP(); - // Drop CAP_IPC_OWNER which allows us to bypass permissions. AutoCapability cap(CAP_IPC_OWNER, false); @@ -749,8 +745,6 @@ TEST(MsgqueueTest, MsgCtlIpcStatWriteOnly) { // Test msgctl with IPC_SET option. TEST(MsgqueueTest, MsgCtlIpcSet) { - GTEST_SKIP(); - Queue queue(msgget(IPC_PRIVATE, 0600)); ASSERT_THAT(queue.get(), SyscallSucceeds()); @@ -767,8 +761,6 @@ TEST(MsgqueueTest, MsgCtlIpcSet) { // Test increasing msg_qbytes beyond limit with IPC_SET. TEST(MsgqueueTest, MsgCtlIpcSetMaxBytes) { - GTEST_SKIP(); - // Drop CAP_SYS_RESOURCE which allows us to increase msg_qbytes beyond the // system parameter MSGMNB. AutoCapability cap(CAP_SYS_RESOURCE, false); @@ -792,8 +784,6 @@ TEST(MsgqueueTest, MsgCtlIpcSetMaxBytes) { // Test msgctl with IPC_INFO option. TEST(MsgqueueTest, MsgCtlIpcInfo) { - GTEST_SKIP(); - struct msginfo info; ASSERT_THAT(msgctl(0, IPC_INFO, reinterpret_cast(&info)), SyscallSucceeds()); @@ -809,8 +799,6 @@ TEST(MsgqueueTest, MsgCtlIpcInfo) { // Test msgctl with MSG_INFO option. TEST(MsgqueueTest, MsgCtlMsgInfo) { - GTEST_SKIP(); - struct msginfo info; ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast(&info)), SyscallSucceeds()); -- cgit v1.2.3