mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement command IPC_STAT for semctl.
PiperOrigin-RevId: 339166854
This commit is contained in:
@@ -283,6 +283,33 @@ func (s *Set) Change(ctx context.Context, creds *auth.Credentials, owner fs.File
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStat extracts semid_ds information from the set.
|
||||
func (s *Set) GetStat(creds *auth.Credentials) (*linux.SemidDS, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// "The calling process must have read permission on the semaphore set."
|
||||
if !s.checkPerms(creds, fs.PermMask{Read: true}) {
|
||||
return nil, syserror.EACCES
|
||||
}
|
||||
|
||||
ds := &linux.SemidDS{
|
||||
SemPerm: linux.IPCPerm{
|
||||
Key: uint32(s.key),
|
||||
UID: uint32(creds.UserNamespace.MapFromKUID(s.owner.UID)),
|
||||
GID: uint32(creds.UserNamespace.MapFromKGID(s.owner.GID)),
|
||||
CUID: uint32(creds.UserNamespace.MapFromKUID(s.creator.UID)),
|
||||
CGID: uint32(creds.UserNamespace.MapFromKGID(s.creator.GID)),
|
||||
Mode: uint16(s.perms.LinuxMode()),
|
||||
Seq: 0, // IPC sequence not supported.
|
||||
},
|
||||
SemOTime: s.opTime.TimeT(),
|
||||
SemCTime: s.changeTime.TimeT(),
|
||||
SemNSems: uint64(s.Size()),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
// SetVal overrides a semaphore value, waking up waiters as needed.
|
||||
func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Credentials, pid int32) error {
|
||||
if val < 0 || val > valueMax {
|
||||
@@ -320,7 +347,7 @@ func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credenti
|
||||
}
|
||||
|
||||
for _, val := range vals {
|
||||
if val < 0 || val > valueMax {
|
||||
if val > valueMax {
|
||||
return syserror.ERANGE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
63: syscalls.Supported("uname", Uname),
|
||||
64: syscalls.Supported("semget", Semget),
|
||||
65: syscalls.PartiallySupported("semop", Semop, "Option SEM_UNDO not supported.", nil),
|
||||
66: syscalls.PartiallySupported("semctl", Semctl, "Options IPC_INFO, SEM_INFO, IPC_STAT, SEM_STAT, SEM_STAT_ANY, GETNCNT, GETZCNT not supported.", nil),
|
||||
66: syscalls.PartiallySupported("semctl", Semctl, "Options IPC_INFO, SEM_INFO, SEM_STAT, SEM_STAT_ANY, GETNCNT, GETZCNT not supported.", nil),
|
||||
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)
|
||||
@@ -619,7 +619,7 @@ var ARM64 = &kernel.SyscallTable{
|
||||
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)
|
||||
190: syscalls.Supported("semget", Semget),
|
||||
191: syscalls.PartiallySupported("semctl", Semctl, "Options IPC_INFO, SEM_INFO, IPC_STAT, SEM_STAT, SEM_STAT_ANY, GETNCNT, GETZCNT not supported.", nil),
|
||||
191: syscalls.PartiallySupported("semctl", Semctl, "Options IPC_INFO, SEM_INFO, SEM_STAT, SEM_STAT_ANY, GETNCNT, GETZCNT not supported.", nil),
|
||||
192: syscalls.ErrorWithEvent("semtimedop", syserror.ENOSYS, "", []string{"gvisor.dev/issue/137"}),
|
||||
193: syscalls.PartiallySupported("semop", Semop, "Option SEM_UNDO not supported.", nil),
|
||||
194: syscalls.PartiallySupported("shmget", Shmget, "Option SHM_HUGETLB is not supported.", nil),
|
||||
|
||||
@@ -129,9 +129,17 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
v, err := getPID(t, id, num)
|
||||
return uintptr(v), nil, err
|
||||
|
||||
case linux.IPC_STAT:
|
||||
arg := args[3].Pointer()
|
||||
ds, err := ipcStat(t, id)
|
||||
if err == nil {
|
||||
_, err = ds.CopyOut(t, arg)
|
||||
}
|
||||
|
||||
return 0, nil, err
|
||||
|
||||
case linux.IPC_INFO,
|
||||
linux.SEM_INFO,
|
||||
linux.IPC_STAT,
|
||||
linux.SEM_STAT,
|
||||
linux.SEM_STAT_ANY,
|
||||
linux.GETNCNT,
|
||||
@@ -171,6 +179,16 @@ func ipcSet(t *kernel.Task, id int32, uid auth.UID, gid auth.GID, perms fs.FileP
|
||||
return set.Change(t, creds, owner, perms)
|
||||
}
|
||||
|
||||
func ipcStat(t *kernel.Task, id int32) (*linux.SemidDS, error) {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByID(id)
|
||||
if set == nil {
|
||||
return nil, syserror.EINVAL
|
||||
}
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
return set.GetStat(creds)
|
||||
}
|
||||
|
||||
func setVal(t *kernel.Task, id int32, num int32, val int16) error {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByID(id)
|
||||
|
||||
@@ -486,6 +486,62 @@ TEST(SemaphoreTest, SemIpcSet) {
|
||||
ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EACCES));
|
||||
}
|
||||
|
||||
TEST(SemaphoreTest, SemCtlIpcStat) {
|
||||
// Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions.
|
||||
ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false));
|
||||
const uid_t kUid = getuid();
|
||||
const gid_t kGid = getgid();
|
||||
time_t start_time = time(nullptr);
|
||||
|
||||
AutoSem sem(semget(IPC_PRIVATE, 10, 0600 | IPC_CREAT));
|
||||
ASSERT_THAT(sem.get(), SyscallSucceeds());
|
||||
|
||||
struct semid_ds ds;
|
||||
EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), SyscallSucceeds());
|
||||
|
||||
EXPECT_EQ(ds.sem_perm.__key, IPC_PRIVATE);
|
||||
EXPECT_EQ(ds.sem_perm.uid, kUid);
|
||||
EXPECT_EQ(ds.sem_perm.gid, kGid);
|
||||
EXPECT_EQ(ds.sem_perm.cuid, kUid);
|
||||
EXPECT_EQ(ds.sem_perm.cgid, kGid);
|
||||
EXPECT_EQ(ds.sem_perm.mode, 0600);
|
||||
// Last semop time is not set on creation.
|
||||
EXPECT_EQ(ds.sem_otime, 0);
|
||||
EXPECT_GE(ds.sem_ctime, start_time);
|
||||
EXPECT_EQ(ds.sem_nsems, 10);
|
||||
|
||||
// The timestamps only have a resolution of seconds; slow down so we actually
|
||||
// see the timestamps change.
|
||||
absl::SleepFor(absl::Seconds(1));
|
||||
|
||||
// Set semid_ds structure of the set.
|
||||
auto last_ctime = ds.sem_ctime;
|
||||
start_time = time(nullptr);
|
||||
struct semid_ds semid_to_set = {};
|
||||
semid_to_set.sem_perm.uid = kUid;
|
||||
semid_to_set.sem_perm.gid = kGid;
|
||||
semid_to_set.sem_perm.mode = 0666;
|
||||
ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &semid_to_set), SyscallSucceeds());
|
||||
struct sembuf buf = {};
|
||||
buf.sem_op = 1;
|
||||
ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds());
|
||||
|
||||
EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), SyscallSucceeds());
|
||||
EXPECT_EQ(ds.sem_perm.mode, 0666);
|
||||
EXPECT_GE(ds.sem_otime, start_time);
|
||||
EXPECT_GT(ds.sem_ctime, last_ctime);
|
||||
|
||||
// An invalid semid fails the syscall with errno EINVAL.
|
||||
EXPECT_THAT(semctl(sem.get() + 1, 0, IPC_STAT, &ds),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
|
||||
// Make semaphore not readable and check the signal fails.
|
||||
semid_to_set.sem_perm.mode = 0200;
|
||||
ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &semid_to_set), SyscallSucceeds());
|
||||
EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds),
|
||||
SyscallFailsWithErrno(EACCES));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
Reference in New Issue
Block a user