diff --git a/pkg/sentry/kernel/task_acct.go b/pkg/sentry/kernel/task_acct.go index 24319692d8..9fe857390d 100644 --- a/pkg/sentry/kernel/task_acct.go +++ b/pkg/sentry/kernel/task_acct.go @@ -148,7 +148,9 @@ type rlimitCPUSoftListener struct { // NotifyTimer implements ktime.Listener.NotifyTimer. func (l *rlimitCPUSoftListener) NotifyTimer(exp uint64) { - l.tg.appSysCPUClockLast.Load().SendGroupSignal(SignalInfoPriv(linux.SIGXCPU)) + if t := l.tg.appSysCPUClockLast.Load(); t != nil { // May be nil during thread group creation. + t.SendGroupSignal(SignalInfoPriv(linux.SIGXCPU)) + } } // +stateify savable diff --git a/test/syscalls/linux/timers.cc b/test/syscalls/linux/timers.cc index 4b215bf10a..230ddbfb08 100644 --- a/test/syscalls/linux/timers.cc +++ b/test/syscalls/linux/timers.cc @@ -298,6 +298,44 @@ TEST(TimerTest, RlimitCpuInheritedAcrossFork) { << "status = " << status; } +TEST(TimerTest, RlimitCpuSoftLimitExceededOnFork) { + pid_t child_pid = fork(); + MaybeSave(); + if (child_pid == 0) { + // Ignore SIGXCPU from the RLIMIT_CPU soft limit. + struct sigaction new_action; + new_action.sa_handler = NoopSignalHandler; + new_action.sa_flags = 0; + sigemptyset(&new_action.sa_mask); + TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0); + + // Set hard limit to expire a short time from now. + struct timespec ts; + TEST_PCHECK(clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0); + struct rlimit cpu_limits; + cpu_limits.rlim_cur = ts.tv_sec; // Exceeded immediately. + cpu_limits.rlim_max = ts.tv_sec + 1; + TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); + MaybeSave(); + pid_t grandchild_pid = fork(); + MaybeSave(); + if (grandchild_pid == 0) { + _exit(0); + } + TEST_PCHECK(grandchild_pid > 0); + int status; + TEST_PCHECK(waitpid(grandchild_pid, &status, 0) == grandchild_pid); + TEST_PCHECK(WIFEXITED(status) && (WEXITSTATUS(status) == 0)); + _exit(0); + } + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && (WEXITSTATUS(status) == 0)) + << "child status = " << status; +} + // See timerfd.cc:TimerSlack() for rationale. constexpr absl::Duration kTimerSlack = absl::Milliseconds(500);