diff options
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_time.go | 9 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_timerfd.go | 2 | ||||
-rw-r--r-- | test/syscalls/linux/clock_gettime.cc | 7 | ||||
-rw-r--r-- | test/syscalls/linux/timerfd.cc | 76 | ||||
-rw-r--r-- | test/syscalls/linux/vdso_clock_gettime.cc | 5 | ||||
-rw-r--r-- | vdso/vdso.cc | 5 |
6 files changed, 68 insertions, 36 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_time.go b/pkg/sentry/syscalls/linux/sys_time.go index fe8725191..4b3f043a2 100644 --- a/pkg/sentry/syscalls/linux/sys_time.go +++ b/pkg/sentry/syscalls/linux/sys_time.go @@ -121,8 +121,15 @@ func getClock(t *kernel.Task, clockID int32) (ktime.Clock, error) { switch clockID { case linux.CLOCK_REALTIME, linux.CLOCK_REALTIME_COARSE: return t.Kernel().RealtimeClock(), nil - case linux.CLOCK_MONOTONIC, linux.CLOCK_MONOTONIC_COARSE, linux.CLOCK_MONOTONIC_RAW: + case linux.CLOCK_MONOTONIC, linux.CLOCK_MONOTONIC_COARSE, + linux.CLOCK_MONOTONIC_RAW, linux.CLOCK_BOOTTIME: // CLOCK_MONOTONIC approximates CLOCK_MONOTONIC_RAW. + // CLOCK_BOOTTIME is internally mapped to CLOCK_MONOTONIC, as: + // - CLOCK_BOOTTIME should behave as CLOCK_MONOTONIC while also + // including suspend time. + // - gVisor has no concept of suspend/resume. + // - CLOCK_MONOTONIC already includes save/restore time, which is + // the closest to suspend time. return t.Kernel().MonotonicClock(), nil case linux.CLOCK_PROCESS_CPUTIME_ID: return t.ThreadGroup().CPUClock(), nil diff --git a/pkg/sentry/syscalls/linux/sys_timerfd.go b/pkg/sentry/syscalls/linux/sys_timerfd.go index 1ce5ce4c3..cf49b43db 100644 --- a/pkg/sentry/syscalls/linux/sys_timerfd.go +++ b/pkg/sentry/syscalls/linux/sys_timerfd.go @@ -37,7 +37,7 @@ func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel switch clockID { case linux.CLOCK_REALTIME: c = t.Kernel().RealtimeClock() - case linux.CLOCK_MONOTONIC: + case linux.CLOCK_MONOTONIC, linux.CLOCK_BOOTTIME: c = t.Kernel().MonotonicClock() default: return 0, nil, syserror.EINVAL diff --git a/test/syscalls/linux/clock_gettime.cc b/test/syscalls/linux/clock_gettime.cc index 335a38d41..c9e3ed6b2 100644 --- a/test/syscalls/linux/clock_gettime.cc +++ b/test/syscalls/linux/clock_gettime.cc @@ -132,6 +132,9 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) { return "CLOCK_MONOTONIC_COARSE"; case CLOCK_MONOTONIC_RAW: return "CLOCK_MONOTONIC_RAW"; + case CLOCK_BOOTTIME: + // CLOCK_BOOTTIME is a monotonic clock. + return "CLOCK_BOOTTIME"; default: return absl::StrCat(info.param); } @@ -140,15 +143,13 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) { INSTANTIATE_TEST_SUITE_P(ClockGettime, MonotonicClockTest, ::testing::Values(CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, - CLOCK_MONOTONIC_RAW), + CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIME), PrintClockId); TEST(ClockGettime, UnimplementedReturnsEINVAL) { SKIP_IF(!IsRunningOnGvisor()); struct timespec tp; - EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME, &tp), - SyscallFailsWithErrno(EINVAL)); EXPECT_THAT(clock_gettime(CLOCK_REALTIME_ALARM, &tp), SyscallFailsWithErrno(EINVAL)); EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME_ALARM, &tp), diff --git a/test/syscalls/linux/timerfd.cc b/test/syscalls/linux/timerfd.cc index 9df53612f..86ed87b7c 100644 --- a/test/syscalls/linux/timerfd.cc +++ b/test/syscalls/linux/timerfd.cc @@ -44,21 +44,24 @@ PosixErrorOr<FileDescriptor> TimerfdCreate(int clockid, int flags) { // // - Because clock_gettime(CLOCK_MONOTONIC) is implemented through the VDSO, // it technically uses a closely-related, but distinct, time domain from the -// CLOCK_MONOTONIC used to trigger timerfd expirations. +// CLOCK_MONOTONIC used to trigger timerfd expirations. The same applies to +// CLOCK_BOOTTIME which is an alias for CLOCK_MONOTONIC. absl::Duration TimerSlack() { return absl::Milliseconds(500); } -TEST(TimerfdTest, IsInitiallyStopped) { - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); +class TimerfdTest : public ::testing::TestWithParam<int> {}; + +TEST_P(TimerfdTest, IsInitiallyStopped) { + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); struct itimerspec its = {}; ASSERT_THAT(timerfd_gettime(tfd.get(), &its), SyscallSucceeds()); EXPECT_EQ(0, its.it_value.tv_sec); EXPECT_EQ(0, its.it_value.tv_nsec); } -TEST(TimerfdTest, SingleShot) { +TEST_P(TimerfdTest, SingleShot) { constexpr absl::Duration kDelay = absl::Seconds(1); - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); struct itimerspec its = {}; its.it_value = absl::ToTimespec(kDelay); ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), @@ -72,11 +75,11 @@ TEST(TimerfdTest, SingleShot) { EXPECT_EQ(1, val); } -TEST(TimerfdTest, Periodic) { +TEST_P(TimerfdTest, Periodic) { constexpr absl::Duration kDelay = absl::Seconds(1); constexpr int kPeriods = 3; - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); struct itimerspec its = {}; its.it_value = absl::ToTimespec(kDelay); its.it_interval = absl::ToTimespec(kDelay); @@ -92,10 +95,10 @@ TEST(TimerfdTest, Periodic) { EXPECT_GE(val, kPeriods); } -TEST(TimerfdTest, BlockingRead) { +TEST_P(TimerfdTest, BlockingRead) { constexpr absl::Duration kDelay = absl::Seconds(3); - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0)); + auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); struct itimerspec its = {}; its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); auto const start_time = absl::Now(); @@ -111,11 +114,11 @@ TEST(TimerfdTest, BlockingRead) { EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay); } -TEST(TimerfdTest, NonblockingRead_NoRandomSave) { +TEST_P(TimerfdTest, NonblockingRead_NoRandomSave) { constexpr absl::Duration kDelay = absl::Seconds(5); auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); // Since the timer is initially disabled and has never fired, read should // return EAGAIN. @@ -148,11 +151,11 @@ TEST(TimerfdTest, NonblockingRead_NoRandomSave) { SyscallFailsWithErrno(EAGAIN)); } -TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) { +TEST_P(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) { constexpr absl::Duration kDelay = absl::Seconds(3); auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); struct itimerspec its = {}; its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); auto const start_time = absl::Now(); @@ -181,15 +184,15 @@ TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) { SyscallFailsWithErrno(EAGAIN)); } -TEST(TimerfdTest, SetAbsoluteTime) { +TEST_P(TimerfdTest, SetAbsoluteTime) { constexpr absl::Duration kDelay = absl::Seconds(3); // Use a non-blocking timerfd so that if TFD_TIMER_ABSTIME is incorrectly // non-functional, we get EAGAIN rather than a test timeout. auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); struct itimerspec its = {}; - ASSERT_THAT(clock_gettime(CLOCK_MONOTONIC, &its.it_value), SyscallSucceeds()); + ASSERT_THAT(clock_gettime(GetParam(), &its.it_value), SyscallSucceeds()); its.it_value.tv_sec += absl::ToInt64Seconds(kDelay); ASSERT_THAT(timerfd_settime(tfd.get(), TFD_TIMER_ABSTIME, &its, nullptr), SyscallSucceeds()); @@ -201,7 +204,34 @@ TEST(TimerfdTest, SetAbsoluteTime) { EXPECT_EQ(1, val); } -TEST(TimerfdTest, ClockRealtime) { +TEST_P(TimerfdTest, IllegalReadWrite) { + auto const tfd = + ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); + uint64_t val = 0; + EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0), + SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)), + SyscallFailsWithErrno(EINVAL)); + EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0), + SyscallFailsWithErrno(ESPIPE)); +} + +std::string PrintClockId(::testing::TestParamInfo<int> info) { + switch (info.param) { + case CLOCK_MONOTONIC: + return "CLOCK_MONOTONIC"; + case CLOCK_BOOTTIME: + return "CLOCK_BOOTTIME"; + default: + return absl::StrCat(info.param); + } +} + +INSTANTIATE_TEST_SUITE_P(AllTimerTypes, TimerfdTest, + ::testing::Values(CLOCK_MONOTONIC, CLOCK_BOOTTIME), + PrintClockId); + +TEST(TimerfdClockRealtimeTest, ClockRealtime) { // Since CLOCK_REALTIME can, by definition, change, we can't make any // non-flaky assertions about the amount of time it takes for a // CLOCK_REALTIME-based timer to expire. Just check that it expires at all, @@ -220,18 +250,6 @@ TEST(TimerfdTest, ClockRealtime) { EXPECT_EQ(1, val); } -TEST(TimerfdTest, IllegalReadWrite) { - auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK)); - uint64_t val = 0; - EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0), - SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0), - SyscallFailsWithErrno(ESPIPE)); -} - } // namespace } // namespace testing diff --git a/test/syscalls/linux/vdso_clock_gettime.cc b/test/syscalls/linux/vdso_clock_gettime.cc index 759a50569..40c0014b9 100644 --- a/test/syscalls/linux/vdso_clock_gettime.cc +++ b/test/syscalls/linux/vdso_clock_gettime.cc @@ -39,6 +39,8 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) { return "CLOCK_MONOTONIC"; case CLOCK_REALTIME: return "CLOCK_REALTIME"; + case CLOCK_BOOTTIME: + return "CLOCK_BOOTTIME"; default: return absl::StrCat(info.param); } @@ -95,7 +97,8 @@ TEST_P(CorrectVDSOClockTest, IsCorrect) { } INSTANTIATE_TEST_SUITE_P(ClockGettime, CorrectVDSOClockTest, - ::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME), + ::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME, + CLOCK_BOOTTIME), PrintClockId); } // namespace diff --git a/vdso/vdso.cc b/vdso/vdso.cc index 6265ad217..8bb80a7a4 100644 --- a/vdso/vdso.cc +++ b/vdso/vdso.cc @@ -33,6 +33,8 @@ int __common_clock_gettime(clockid_t clock, struct timespec* ts) { ret = ClockRealtime(ts); break; + case CLOCK_BOOTTIME: + // Fallthrough, CLOCK_BOOTTIME is an alias for CLOCK_MONOTONIC case CLOCK_MONOTONIC: ret = ClockMonotonic(ts); break; @@ -122,7 +124,8 @@ extern "C" int __kernel_clock_getres(clockid_t clock, struct timespec* res) { switch (clock) { case CLOCK_REALTIME: - case CLOCK_MONOTONIC: { + case CLOCK_MONOTONIC: + case CLOCK_BOOTTIME: { res->tv_sec = 0; res->tv_nsec = 1; break; |