{"id":29242,"date":"2026-09-25T20:32:13","date_gmt":"2026-09-25T12:32:13","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-98087"},"modified":"2026-09-25T20:32:13","modified_gmt":"2026-09-25T12:32:13","slug":"cve-2026-98087","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-98087","title":{"rendered":"CVE-2026-98087 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nsched\/rt,dl: Skip mig"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>sched\/rt,dl: Skip migrate-disabled tasks when picking a push candidate<\/p>\n<p>A migrate_disable()&#8217;d RT task cannot be moved to another CPU, but the<br \/>\nscheduler still keeps such a task on that CPU&#8217;s pushable list<br \/>\n(rq-&gt;rt.pushable_tasks) and still marks the runqueue RT-overloaded<br \/>\n(rq-&gt;rt.overloaded = 1). So the RT balancer keeps treating this CPU as<br \/>\nhaving a task to move away, and keeps trying to move the task, but the<br \/>\npush can never succeed. When the head is pinned, push_rt_task() does not<br \/>\ngive up either. It falls back to pushing rq-&gt;curr instead, using the<br \/>\nper-CPU stopper, as added by commit a7c81556ec4d (&#8220;sched: Fix<br \/>\nmigrate_disable() vs rt\/dl balancing&#8221;).<\/p>\n<p>The CPU spends tens of milliseconds in this retry loop. The core is<br \/>\nisolated for real-time work, but during the loop nearly half of its time<br \/>\nis consumed by pushes that cannot succeed.<\/p>\n<p>An ftrace capture of the affected CPU, with sched_switch enabled and<br \/>\ncommit 94894c9c477e (&#8220;sched\/rt: Skip currently executing CPU in<br \/>\nrto_next_cpu()&#8221;) applied, shows where the CPU time went. Two SCHED_FIFO<br \/>\ntasks at equal priority shared the CPU, taskA migrate_disable()&#8217;d and<br \/>\nqueued, taskB as rq-&gt;curr. In one 89 ms window, taskB got only 52 ms of<br \/>\nCPU. The other 37 ms went to the stopper thread.<\/p>\n<p>The scheduler kept trying to push taskA, the pinned head of the pushable<br \/>\nlist, fell back to pushing taskB instead, and woke the stopper 5204<br \/>\ntimes. Every one of those pushes failed and no task was moved. taskA<br \/>\nstayed runnable and queued the whole time, and never ran.<\/p>\n<p>Pushing taskB fails on a re-check. find_lock_lowest_rq() drops the rq<br \/>\nlock to take the target rq lock, then checks again with<br \/>\n&#8220;task != pick_next_pushable_task(rq)&#8221;.<\/p>\n<p>The task being pushed is taskB, but the pick returns taskA, the head of<br \/>\nthe pushable list. taskB is rq-&gt;curr, and set_next_task_rt() removes the<br \/>\nrunning task from that list, so taskB can never be the head. The check<br \/>\nexpects a candidate taken from the pushable list, but the fallback<br \/>\npushes rq-&gt;curr, which is never on that list. So the check fails every<br \/>\ntime.<\/p>\n<p>   .&#8211;&gt; push-IPI arrives<br \/>\n   |          |<br \/>\n   |          v<br \/>\n   |     pushable head = taskA   -&gt;  pinned, cannot be pushed<br \/>\n   |          |<br \/>\n   |          v<br \/>\n   |     so push taskB instead   -&gt;  wake migration\/N, a stop-class<br \/>\n   |          |                      thread, so it preempts taskB<br \/>\n   |          v<br \/>\n   |     re-check compares taskB against the pushable head,<br \/>\n   |     which is still taskA                        -&gt;  give up<br \/>\n   |          |<br \/>\n   |          v<br \/>\n   |     nothing moved, taskA still queued, rq still overloaded<br \/>\n   |          |<br \/>\n   &#8216;&#8212;&#8212;&#8212;-&#8216;<br \/>\n         repeats every ~17 us, 5204 times, for 89 ms<\/p>\n<p>   The loop cannot stop itself. Every round leaves the runqueue<br \/>\n   exactly as it was, so the next push-IPI does the same thing. In<br \/>\n   the capture it ended only when taskB went to sleep on its own.<br \/>\n   taskA was then picked locally and left the pushable list.<\/p>\n<p>   CPU time per task in the window, from sched_switch:<\/p>\n<p>     taskB         51.95 ms   real work<br \/>\n     migration\/N   37.18 ms   nothing moved<br \/>\n     taskA          0.00 ms   queued the whole time, never picked<br \/>\n     idle           0.01 ms<\/p>\n<p>   Counts over the same window:<\/p>\n<p>      7667  push-IPIs handled on this CPU<br \/>\n     17481  pick_next_pushable_task() returned taskA, still pinned<br \/>\n      5204  find_lock_lowest_rq() gave up on the re-check<br \/>\n         1  push that actually completed<br \/>\n         0  migrations of taskA<\/p>\n<p>   The CPU times and the window length come from the standard<br \/>\n   sched_switch tracepoint. The counts needed tracepoints added inside<br \/>\n   the RT balancer for this investigation.<\/p>\n<p>The self-IPI path is closed by the rto_next_cpu() fix above, and that<br \/>\npart works. But the runqueue is still marked overloaded, because the<br \/>\npinned task is still advertised as pushable. Other CPUs now send the<br \/>\npush-IPIs during their own RT balancing, and the same loop runs again.<br \/>\nClosing the self-IPI path did not stop a pinn<br \/>\n&#8212;truncated&#8212;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>sched\/rt,dl: Skip migrate-disabled tasks when picking a push candidate<\/p>\n<p>A migrate_disable()&#8217;d RT task cannot be moved to another CP<\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-29242","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/29242","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=29242"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=29242"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=29242"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}