{"id":"CVE-2026-99205","aliases":[],"title":"sched/core: Call wakeup_preempt() for queued tasks during class change","titleSource":"Linux","descriptionSource":"Linux","products":[{"vendor":"Linux","name":"Linux","platforms":[],"versions":[{"version":"704069649b5bfb7bf1fe32c0281fe9036806a59a","status":"affected","lessThan":"ae89744c5ebf299bf55ac7581768fbd9f119a903","versionType":"git"},{"version":"704069649b5bfb7bf1fe32c0281fe9036806a59a","status":"affected","lessThan":"118a2cf4ced60777a2408001e2b4838fed1cea55","versionType":"git"}],"defaultStatus":"unaffected","source":"Linux","url":"https://www.cve.org/CVERecord?id=CVE-2026-99205"},{"vendor":"Linux","name":"Linux","platforms":[],"versions":[{"version":"7.0","status":"affected"},{"version":"0","status":"unaffected","lessThan":"7.0","versionType":"semver"},{"version":"7.2.13","status":"unaffected","lessThanOrEqual":"7.2.*","versionType":"semver"},{"version":"7.3","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"defaultStatus":"affected","source":"Linux","url":"https://www.cve.org/CVERecord?id=CVE-2026-99205"}],"remediation":[],"description":"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---","severity":"unknown","score":null,"published":"2026-10-28T10:35:59.093Z","modified":"2026-10-28T10:35:59.093Z","withdrawn":false,"knownExploited":false,"affected":[],"references":["https://git.kernel.org/stable/c/ae89744c5ebf299bf55ac7581768fbd9f119a903","https://git.kernel.org/stable/c/118a2cf4ced60777a2408001e2b4838fed1cea55"],"sources":[{"name":"CVE","id":"CVE-2026-99205","modified":"2026-10-28T10:35:59.093Z","url":"https://www.cve.org/CVERecord?id=CVE-2026-99205"}],"assessments":[],"sourceAffected":[],"kev":{"status":"not-listed","checkedAt":"2026-10-28T23:15:32.205Z"},"coverage":[{"source":"NVD","status":"not-checked"},{"source":"GHSA","status":"not-checked"},{"source":"EUVD","status":"not-checked"},{"source":"EPSS","status":"not-checked"}],"evidence":[]}