CVE-2026-99205
Full vulnerability record from the local CVENow database.
Description
In the Linux kernel, the following vulnerability has been resolved: sched/core: Call wakeup_preempt() 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->next_class = &fair_sched_class rq->nr_running = 1 rq->cfs.h_nr_queued = 0 rq->rt.rt_nr_running = 1 Although the waiting task was an RT task, "rq->next_class" did not point to rt_sched_class and was stuck at the lower priority fair_sched_class. Inspecting sched_change_end() revealed the following race with __sched_setscheduler() for queued, but non-running tasks: CPU0 CPU1 ==== ==== __schedule() /* * rq->donor is a fair task that blocks. * rq->next_class = &fair_sched_class */ rq_lock(rq0) pick_next_task() /* nr_running = 0 */ pick_task_fair() sched_balance_newidle() raw_spin_rq_unlock(rq0) rq_lock(rq0) activate_task(rq0, p) /* p is a fair task */ rq0->nr_running++; rq0->cfs.h_nr_queued++; wakeup_preempt(rq, p) rq->next_class = &fair_sched_class resched_curr() rq_unlock(rq0) __sched_setscheduler(p) /* p is moved to SCHED_RR */ task_rq_lock(p) sched_change_begin() rq0->cfs.h_nr_queued--; rq0->rt.rt_nr_running++; sched_change_end() /* * p->on_rq = 1 (ctx->queued) * but p is not rq->donor * (!ctx->running) hence * resched_curr() is and * rq->next_class upgrade is * skipped. */ task_rq_unlock() raw_spin_rq_lock(rq0) /* * rq->nr_running = 1 * rq->cfs.h_nr_queued = 0 * rq->rt.rt_nr_running = 1 * rq->next_class = &fair_sched_class */ if (rq->cfs.h_nr_queued && ...) /* Above is false */ if (rq_modified_above(this_rq, &fair_sched_class)) /* Also false since rq->next_class is still fair */ /* * sched_balance_newidle() is considered to * have failed to pull any fair tasks and no * other wakeups were detected. */ return NULL; /* pick_task_fair() */ pick_task_idle() return rq->idle; next = rq->idle; /* Preemption indicators are lost */ clear_tsk_need_resched(prev); clear_preempt_need_resched(); rq->curr = rq->idle; !!! rq->idle switches in with rq->running == 1 but !need_resched() !!! In simple words, enqueue, followed by a class change of the newly queued task goes unnoticed in rq->next_class indicator and sched_balance_newidle() 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 wakeup_preempt() for ctx->queued cases in sched_change_end() to re-evaluate rq->next_class after class change (ENQUEUE_CLASS) and set the NEED_RESCHED indicator accordingly. Newidle balance will correctly see an enqueue of a higher priority t ---truncated---
View original CVE JSON
{
"id": "CVE-2026-99205",
"sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"published": "2026-10-28T11:17:41.773",
"lastModified": "2026-10-28T11:17:41.773",
"vulnStatus": "Received",
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "In the Linux kernel, the following vulnerability has been resolved:\n\nsched/core: Call wakeup_preempt() for queued tasks during class change\n\nPaul reported recent RCU changes exposed a lost resched where CPU idles\nwith a rcutorture thread waiting while being completely runnable.\n\nDumping the rq state at the time of idling with runnable tasks showed\nthe following:\n\n rq->next_class = &fair_sched_class\n rq->nr_running = 1\n rq->cfs.h_nr_queued = 0\n rq->rt.rt_nr_running = 1\n\nAlthough the waiting task was an RT task, \"rq->next_class\" did not point\nto rt_sched_class and was stuck at the lower priority fair_sched_class.\n\nInspecting sched_change_end() revealed the following race with\n__sched_setscheduler() for queued, but non-running tasks:\n\n CPU0 CPU1\n ==== ====\n\n __schedule()\n\n /*\n * rq->donor is a fair task that blocks.\n * rq->next_class = &fair_sched_class\n */\n\n rq_lock(rq0)\n\n pick_next_task()\n /* nr_running = 0 */\n pick_task_fair()\n sched_balance_newidle()\n raw_spin_rq_unlock(rq0)\n rq_lock(rq0)\n\n activate_task(rq0, p)\n /* p is a fair task */\n rq0->nr_running++;\n rq0->cfs.h_nr_queued++;\n wakeup_preempt(rq, p)\n rq->next_class = &fair_sched_class\n resched_curr()\n\n rq_unlock(rq0)\n\n __sched_setscheduler(p)\n /* p is moved to SCHED_RR */\n task_rq_lock(p)\n sched_change_begin()\n\n\t\t\t\t rq0->cfs.h_nr_queued--;\n\t\t\t\t rq0->rt.rt_nr_running++;\n\n sched_change_end()\n /*\n * p->on_rq = 1 (ctx->queued)\n * but p is not rq->donor\n * (!ctx->running) hence\n * resched_curr() is and\n * rq->next_class upgrade is\n * skipped.\n */\n\n task_rq_unlock()\n\n raw_spin_rq_lock(rq0)\n\n /*\n * rq->nr_running = 1\n * rq->cfs.h_nr_queued = 0\n * rq->rt.rt_nr_running = 1\n * rq->next_class = &fair_sched_class\n */\n\n if (rq->cfs.h_nr_queued && ...)\n /* Above is false */\n\n if (rq_modified_above(this_rq, &fair_sched_class))\n /* Also false since rq->next_class is still fair */\n\n /*\n * sched_balance_newidle() is considered to\n * have failed to pull any fair tasks and no\n * other wakeups were detected.\n */\n return NULL; /* pick_task_fair() */\n\n pick_task_idle()\n return rq->idle;\n\n next = rq->idle;\n\n /* Preemption indicators are lost */\n clear_tsk_need_resched(prev);\n clear_preempt_need_resched();\n\n rq->curr = rq->idle;\n\n !!! rq->idle switches in with rq->running == 1 but !need_resched() !!!\n\nIn simple words, enqueue, followed by a class change of the newly queued\ntask goes unnoticed in rq->next_class indicator and\nsched_balance_newidle() sees no fair task, nor any higher priority task\nenqueued.\n\nThe above sequence will lead to CPU idling with a runnable task on the\nCPU.\n\nCall wakeup_preempt() for ctx->queued cases in sched_change_end() to\nre-evaluate rq->next_class after class change (ENQUEUE_CLASS) and set\nthe NEED_RESCHED indicator accordingly.\n\nNewidle balance will correctly see an enqueue of a higher priority\nt\n---truncated---"
}
],
"affected": [
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"affectedData": [
{
"vendor": "Linux",
"product": "Linux",
"defaultStatus": "unaffected",
"programFiles": [
"kernel/sched/core.c"
],
"repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
"versions": [
{
"version": "704069649b5bfb7bf1fe32c0281fe9036806a59a",
"lessThan": "ae89744c5ebf299bf55ac7581768fbd9f119a903",
"versionType": "git",
"status": "affected"
},
{
"version": "704069649b5bfb7bf1fe32c0281fe9036806a59a",
"lessThan": "118a2cf4ced60777a2408001e2b4838fed1cea55",
"versionType": "git",
"status": "affected"
}
]
},
{
"vendor": "Linux",
"product": "Linux",
"defaultStatus": "affected",
"programFiles": [
"kernel/sched/core.c"
],
"repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
"versions": [
{
"version": "7.0",
"status": "affected"
},
{
"version": "0",
"lessThan": "7.0",
"versionType": "semver",
"status": "unaffected"
},
{
"version": "7.2.13",
"lessThanOrEqual": "7.2.*",
"versionType": "semver",
"status": "unaffected"
},
{
"version": "7.3",
"lessThanOrEqual": "*",
"versionType": "original_commit_for_fix",
"status": "unaffected"
}
]
}
]
}
],
"metrics": {},
"references": [
{
"url": "https://git.kernel.org/stable/c/118a2cf4ced60777a2408001e2b4838fed1cea55",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/ae89744c5ebf299bf55ac7581768fbd9f119a903",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
}
]
}