diff options
-rw-r--r-- | pkg/sentry/syscalls/linux/linux64.go | 4 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_sem.go | 60 |
2 files changed, 49 insertions, 15 deletions
diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index 62d1e8f8b..4747117b8 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -272,7 +272,7 @@ var AMD64 = &kernel.SyscallTable{ 217: syscalls.Supported("getdents64", Getdents64), 218: syscalls.Supported("set_tid_address", SetTidAddress), 219: syscalls.Supported("restart_syscall", RestartSyscall), - 220: syscalls.PartiallySupported("semtimedop", Semtimedop, "A non-zero timeout argument isn't supported.", []string{"gvisor.dev/issue/137"}), + 220: syscalls.Supported("semtimedop", Semtimedop), 221: syscalls.PartiallySupported("fadvise64", Fadvise64, "Not all options are supported.", nil), 222: syscalls.Supported("timer_create", TimerCreate), 223: syscalls.Supported("timer_settime", TimerSettime), @@ -620,7 +620,7 @@ var ARM64 = &kernel.SyscallTable{ 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 SEM_STAT_ANY not supported.", nil), - 192: syscalls.PartiallySupported("semtimedop", Semtimedop, "A non-zero timeout argument isn't supported.", []string{"gvisor.dev/issue/137"}), + 192: syscalls.Supported("semtimedop", Semtimedop), 193: syscalls.PartiallySupported("semop", Semop, "Option SEM_UNDO not supported.", nil), 194: syscalls.PartiallySupported("shmget", Shmget, "Option SHM_HUGETLB is not supported.", nil), 195: syscalls.PartiallySupported("shmctl", Shmctl, "Options SHM_LOCK, SHM_UNLOCK are not supported.", nil), diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go index d324461a3..55287f147 100644 --- a/pkg/sentry/syscalls/linux/sys_sem.go +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -16,6 +16,7 @@ package linux import ( "math" + "time" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/marshal/primitive" @@ -50,11 +51,42 @@ func Semget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal // Semtimedop handles: semop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout) func Semtimedop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { - // TODO(gvisor.dev/issue/137): A non-zero timeout isn't supported. - if args[3].Pointer() != 0 { - return 0, nil, syserror.ENOSYS + // If the timeout argument is NULL, then semtimedop() behaves exactly like semop(). + if args[3].Pointer() == 0 { + return Semop(t, args) } - return Semop(t, args) + + id := args[0].Int() + sembufAddr := args[1].Pointer() + nsops := args[2].SizeT() + timespecAddr := args[3].Pointer() + if nsops <= 0 { + return 0, nil, syserror.EINVAL + } + if nsops > opsMax { + return 0, nil, syserror.E2BIG + } + + ops := make([]linux.Sembuf, nsops) + if _, err := linux.CopySembufSliceIn(t, sembufAddr, ops); err != nil { + return 0, nil, err + } + + var timeout linux.Timespec + if _, err := timeout.CopyIn(t, timespecAddr); err != nil { + return 0, nil, err + } + if timeout.Sec < 0 || timeout.Nsec < 0 || timeout.Nsec >= 1e9 { + return 0, nil, syserror.EINVAL + } + + if err := semTimedOp(t, id, ops, true, timeout.ToDuration()); err != nil { + if err == syserror.ETIMEDOUT { + return 0, nil, syserror.EAGAIN + } + return 0, nil, err + } + return 0, nil, nil } // Semop handles: semop(int semid, struct sembuf *sops, size_t nsops) @@ -63,11 +95,6 @@ func Semop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall sembufAddr := args[1].Pointer() nsops := args[2].SizeT() - r := t.IPCNamespace().SemaphoreRegistry() - set := r.FindByID(id) - if set == nil { - return 0, nil, syserror.EINVAL - } if nsops <= 0 { return 0, nil, syserror.EINVAL } @@ -79,18 +106,25 @@ func Semop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall if _, err := linux.CopySembufSliceIn(t, sembufAddr, ops); err != nil { return 0, nil, err } + return 0, nil, semTimedOp(t, id, ops, false, time.Second) +} +func semTimedOp(t *kernel.Task, id int32, ops []linux.Sembuf, haveTimeout bool, timeout time.Duration) error { + set := t.IPCNamespace().SemaphoreRegistry().FindByID(id) + + if set == nil { + return syserror.EINVAL + } creds := auth.CredentialsFromContext(t) pid := t.Kernel().GlobalInit().PIDNamespace().IDOfThreadGroup(t.ThreadGroup()) for { ch, num, err := set.ExecuteOps(t, ops, creds, int32(pid)) if ch == nil || err != nil { - // We're done (either on success or a failure). - return 0, nil, err + return err } - if err = t.Block(ch); err != nil { + if _, err = t.BlockWithTimeout(ch, haveTimeout, timeout); err != nil { set.AbortWait(num, ch) - return 0, nil, err + return err } } } |