diff options
Diffstat (limited to 'pkg/sentry/syscalls/linux/sys_timer.go')
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_timer.go | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_timer.go b/pkg/sentry/syscalls/linux/sys_timer.go new file mode 100644 index 000000000..4ed077626 --- /dev/null +++ b/pkg/sentry/syscalls/linux/sys_timer.go @@ -0,0 +1,168 @@ +// Copyright 2018 Google Inc. +// +// 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 linux + +import ( + "syscall" + "time" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" +) + +// ItimerType denotes the type of interval timer. +type ItimerType int + +// Interval timer types from <sys/time.h>. +const ( + // ItimerReal equals to ITIMER_REAL. + ItimerReal ItimerType = iota + // ItimerVirtual equals to ITIMER_VIRTUAL. + ItimerVirtual + // ItimerProf equals to ITIMER_PROF. + ItimerProf +) + +const nsecPerSec = int64(time.Second) + +// copyItimerValIn copies an ItimerVal from the untrusted app range to the +// kernel. The ItimerVal may be either 32 or 64 bits. +// A NULL address is allowed because because Linux allows +// setitimer(which, NULL, &old_value) which disables the timer. +// There is a KERN_WARN message saying this misfeature will be removed. +// However, that hasn't happened as of 3.19, so we continue to support it. +func copyItimerValIn(t *kernel.Task, addr usermem.Addr) (linux.ItimerVal, error) { + if addr == usermem.Addr(0) { + return linux.ItimerVal{}, nil + } + + switch t.Arch().Width() { + case 8: + // Native size, just copy directly. + var itv linux.ItimerVal + if _, err := t.CopyIn(addr, &itv); err != nil { + return linux.ItimerVal{}, err + } + + return itv, nil + default: + return linux.ItimerVal{}, syscall.ENOSYS + } +} + +// copyItimerValOut copies an ItimerVal to the untrusted app range. +// The ItimerVal may be either 32 or 64 bits. +// A NULL address is allowed, in which case no copy takes place +func copyItimerValOut(t *kernel.Task, addr usermem.Addr, itv *linux.ItimerVal) error { + if addr == usermem.Addr(0) { + return nil + } + + switch t.Arch().Width() { + case 8: + // Native size, just copy directly. + _, err := t.CopyOut(addr, itv) + return err + default: + return syscall.ENOSYS + } +} + +func findTimer(t *kernel.Task, w ItimerType) (*ktime.Timer, error) { + switch w { + case ItimerReal: + return t.ThreadGroup().Timer().RealTimer, nil + case ItimerVirtual: + return t.ThreadGroup().Timer().VirtualTimer, nil + case ItimerProf: + return t.ThreadGroup().Timer().ProfTimer, nil + default: + return nil, syscall.EINVAL + } +} + +// Getitimer implements linux syscall getitimer(2). +func Getitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + timerID := ItimerType(args[0].Int()) + val := args[1].Pointer() + + timer, err := findTimer(t, timerID) + if err != nil { + return 0, nil, err + } + value, interval := ktime.SpecFromSetting(timer.Get()) + olditv := linux.ItimerVal{ + Value: linux.DurationToTimeval(value), + Interval: linux.DurationToTimeval(interval), + } + + return 0, nil, copyItimerValOut(t, val, &olditv) +} + +// Setitimer implements linux syscall setitimer(2). +func Setitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + timerID := ItimerType(args[0].Int()) + newVal := args[1].Pointer() + oldVal := args[2].Pointer() + + timer, err := findTimer(t, timerID) + if err != nil { + return 0, nil, err + } + + itv, err := copyItimerValIn(t, newVal) + if err != nil { + return 0, nil, err + } + // Just like linux, we cap the timer value and interval with the max + // number that int64 can represent which is roughly 292 years. + s, err := ktime.SettingFromSpec(itv.Value.ToDuration(), + itv.Interval.ToDuration(), timer.Clock()) + if err != nil { + return 0, nil, err + } + + valueNS, intervalNS := ktime.SpecFromSetting(timer.Swap(s)) + olditv := linux.ItimerVal{ + Value: linux.DurationToTimeval(valueNS), + Interval: linux.DurationToTimeval(intervalNS), + } + + return 0, nil, copyItimerValOut(t, oldVal, &olditv) +} + +// Alarm implements linux syscall alarm(2). +func Alarm(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + duration := time.Duration(args[0].Uint()) * time.Second + + timer := t.ThreadGroup().Timer().RealTimer + s, err := ktime.SettingFromSpec(duration, 0, timer.Clock()) + if err != nil { + return 0, nil, err + } + + value, _ := ktime.SpecFromSetting(timer.Swap(s)) + sec := int64(value) / nsecPerSec + nsec := int64(value) % nsecPerSec + // We can't return 0 if we have an alarm pending ... + if (sec == 0 && nsec > 0) || nsec >= nsecPerSec/2 { + sec++ + } + + return uintptr(sec), nil, nil +} |