Do not crash when creating thread group with already-exceeded soft CPU limit.

Reported-by: syzbot+da9595a72d0762aaa48d@syzkaller.appspotmail.com
PiperOrigin-RevId: 699425946
This commit is contained in:
Etienne Perot
2024-11-23 01:28:50 -08:00
committed by gVisor bot
parent d118c2b5ca
commit 2b55090a58
3 changed files with 20 additions and 7 deletions
+12 -1
View File
@@ -213,6 +213,17 @@ func (ts *TaskSet) newTask(ctx context.Context, cfg *TaskConfig) (*Task, error)
}
}
// If the task was the first to be added to the thread group, check if
// it needs to be notified of CPU limits being exceeded.
// We use a defer here because we need to do this without holding the
// TaskSet or signalHandlers lock.
var isFirstTask bool
defer func() {
if isFirstTask {
tg.notifyRlimitCPUUpdated(t)
}
}()
// Make the new task (and possibly thread group) visible to the rest of
// the system atomically.
ts.mu.Lock()
@@ -259,7 +270,7 @@ func (ts *TaskSet) newTask(ctx context.Context, cfg *TaskConfig) (*Task, error)
t.EnterInitialCgroups(srcT, cfg.InitialCgroups)
committed = true
if tg.leader == nil {
if isFirstTask = tg.leader == nil; isFirstTask {
// New thread group.
tg.leader = t
if parentPG := tg.parentPG(); parentPG == nil {
-1
View File
@@ -305,7 +305,6 @@ func (k *Kernel) NewThreadGroup(pidns *PIDNamespace, sh *SignalHandlers, termina
tg.rlimitCPUSoftListener.tg = tg
tg.rlimitCPUHardTimer.Init(&tg.appSysCPUClock, &tg.rlimitCPUHardListener)
tg.rlimitCPUHardListener.tg = tg
tg.notifyRlimitCPUUpdated(nil)
tg.oldRSeqCritical.Store(&OldRSeqCriticalRegion{})
return tg
}
+8 -5
View File
@@ -238,15 +238,18 @@ TEST(TimerTest, RlimitCpuInheritedAcrossFork) {
sigemptyset(&new_action.sa_mask);
TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0);
// Set both soft and hard limits to expire a short time from now. (Since we
// may not be able to raise RLIMIT_CPU again, this must happen in a
// disposable child of the test process.)
constexpr int kDelaySeconds = 2;
struct timespec ts;
TEST_PCHECK(clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0);
struct rlimit cpu_limits;
// Set soft limit to 0 to expire immediately. This should cause
// a SIGXCPU to be sent to the grandchild immediately on fork.
cpu_limits.rlim_cur = 0;
// Set hard limit to expire a short time from now. (Since we
// may not be able to raise RLIMIT_CPU again, this must happen in a
// disposable child of the test process.)
// +1 to round up, presuming that ts.tv_nsec > 0.
cpu_limits.rlim_cur = cpu_limits.rlim_max = ts.tv_sec + kDelaySeconds + 1;
cpu_limits.rlim_max = ts.tv_sec + kDelaySeconds + 1;
TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0);
MaybeSave();
@@ -271,7 +274,7 @@ TEST(TimerTest, RlimitCpuInheritedAcrossFork) {
// block in waitid().
// TODO: b/315388929 - remove this
if (x % 16384 == 0) {
TEST_PCHECK(ppoll(&pfd, 1, &timeout, nullptr) == 0);
TEST_PCHECK(RetryEINTR(ppoll)(&pfd, 1, &timeout, nullptr) == 0);
}
}
}