CVE-2026-99205: sched/core: Call wakeup_preempt() for queued tasks during class change
In the Linux kernel, the following vulnerability has been resolved:
sched/core: Call wakeuppreempt() for queued tasks during class change
Paul reported recent RCU changes exposed a lost resched where CPU idles with a rcutorture thread waiting while being completely runnable.
Dumping the rq state at the time of idling with runnable tasks showed the following:
rq->nextclass = &fairschedclass rq->nrrunning = 1 rq->cfs.hnrqueued = 0 rq->rt.rtnrrunning = 1
Although the waiting task was an RT task, "rq->nextclass" did not point to rtschedclass and was stuck at the lower priority fairschedclass.
Inspecting schedchangeend() revealed the following race with schedsetscheduler() for queued, but non-running tasks:
CPU0 CPU1 ==== ====
schedule()
/ rq->donor is a fair task that blocks. rq->nextclass = &fairschedclass /
rqlock(rq0)
picknexttask() / nrrunning = 0 / picktaskfair() schedbalancenewidle() rawspinrqunlock(rq0) rqlock(rq0)
activatetask(rq0, p) / p is a fair task / rq0->nrrunning++; rq0->cfs.hnrqueued++; wakeuppreempt(rq, p) rq->nextclass = &fairschedclass reschedcurr()
rqunlock(rq0)
schedsetscheduler(p) / p is moved to SCHEDRR / taskrqlock(p) schedchangebegin()
rq0->cfs.hnrqueued--; rq0->rt.rtnrrunning++;
schedchangeend() / p->onrq = 1 (ctx->queued) but p is not rq->donor (!ctx->running) hence reschedcurr() is and rq->nextclass upgrade is skipped. /
taskrqunlock()
rawspinrqlock(rq0)
/ rq->nrrunning = 1 rq->cfs.hnrqueued = 0 rq->rt.rtnrrunning = 1 rq->nextclass = &fairschedclass /
if (rq->cfs.hnrqueued && ...) / Above is false /
if (rqmodifiedabove(thisrq, &fairschedclass)) / Also false since rq->nextclass is still fair /
/ schedbalancenewidle() is considered to have failed to pull any fair tasks and no other wakeups were detected. / return NULL; / picktaskfair() /
picktaskidle() return rq->idle;
next = rq->idle;
/ Preemption indicators are lost / cleartskneedresched(prev); clearpreemptneedresched();
rq->curr = rq->idle;
!!! rq->idle switches in with rq->running == 1 but !needresched() !!!
In simple words, enqueue, followed by a class change of the newly queued task goes unnoticed in rq->nextclass indicator and schedbalancenewidle() sees no fair task, nor any higher priority task enqueued.
The above sequence will lead to CPU idling with a runnable task on the CPU.
Call wakeuppreempt() for ctx->queued cases in schedchangeend() to re-evaluate rq->nextclass after class change (ENQUEUECLASS) and set the NEEDRESCHED indicator accordingly.
Newidle balance will correctly see an enqueue of a higher priority t ---truncated---