{"id":28505,"date":"2026-09-25T00:31:34","date_gmt":"2026-09-24T16:31:34","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-93241"},"modified":"2026-09-25T00:31:34","modified_gmt":"2026-09-24T16:31:34","slug":"cve-2026-93241","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-93241","title":{"rendered":"CVE-2026-93241 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nmemcg: bypass the rec"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>memcg: bypass the reclaim and oom killer for dying tasks once oom_reaper is done<\/p>\n<p>At Meta, we are seeing instances where an OOM killed job is stuck in the<br \/>\nexit path for several hours.  In one particular case, the job was stuck<br \/>\nfor more than 8 hours and I had to manually remove the memory.max limits<br \/>\nto allow the process to exit.<\/p>\n<p>The job was a single process job and had ~55 GiB memory.max and zswap<br \/>\nenabled.  It had almost 0 anon in memory and ~111 GiB in zswap compressed<br \/>\nto ~51 GiB zswap pool (i.e.  almost all of memory.current was zswap).<br \/>\nNothing was left on the LRUs to reclaim.<\/p>\n<p>On further inspection, I observed ~20k threads of that process stuck with<br \/>\nthe following stack:<\/p>\n<p>[] mem_cgroup_out_of_memory+0x4e\/0xa0<br \/>\n[] charge_memcg+0x8bf\/0x990<br \/>\n[] mem_cgroup_swapin_charge_folio+0x4e\/0x80<br \/>\n[] __read_swap_cache_async+0x10c\/0x260<br \/>\n[] swapin_readahead+0x116\/0x3f0<br \/>\n[] do_swap_page+0x13c\/0x1ce0<br \/>\n[] handle_mm_fault+0x61d\/0x11f0<br \/>\n[] do_user_addr_fault+0x3e7\/0x6d0<br \/>\n[] exc_page_fault+0x8f\/0x110<br \/>\n[] asm_exc_page_fault+0x22\/0x30<br \/>\n[] __get_user_8+0x14\/0x20<br \/>\n[] futex_cleanup+0x27\/0x1c0<br \/>\n[] futex_exit_release+0x47\/0x60<br \/>\n[] do_exit+0x107\/0x940<br \/>\n[] do_group_exit+0x81\/0xa0<br \/>\n[] get_signal+0x2b1\/0x6e0<br \/>\n[] arch_do_signal_or_restart+0x1a\/0x1c0<br \/>\n[] exit_to_user_mode_loop+0xa8\/0x1c0<br \/>\n[] do_syscall_64+0x152\/0x250<br \/>\n[] entry_SYSCALL_64_after_hwframe+0x4b\/0x53<\/p>\n<p>In addition the dmesg was filled with &#8220;Out of memory and no killable<br \/>\nprocesses&#8230;&#8221; messages.<\/p>\n<p>I have no idea why oom reaper was not able to reap\/unmap the process.  My<br \/>\nguess is that since oom reaper tries to acquire mmap_lock in read mode<br \/>\nlimited number of times and then gives up, there might be a thread of that<br \/>\nprocess which had mmap_lock in write mode at that time.<\/p>\n<p>My initial suspicion was the futex_cleanup and kernel page fault causing<br \/>\ninfinite fault and charge retries but that was put to rest in previous<br \/>\ndiscussions happened on similar problem [1].<\/p>\n<p>My current theory is that it is just a simple slow serialization behind<br \/>\nthe oom_lock.  Unlike page allocator, memcg charge code takes the oom_lock<br \/>\nwithout the &#8220;try&#8221;.  Though memcg oom code uses mutex_lock_killable(), note<br \/>\nthat in the call stack get_signal() consumes SIGKILL (or<br \/>\nsigdelset(SIGKILL)) before calling do_group_exit().  So this<br \/>\nmutex_lock_killable() is just a mutex_lock() here.  Therefore 10s of<br \/>\nthousands of threads are waiting on oom_lock and one by one they get<br \/>\n-EFAULT from get_user() in the futex cleanup code and bails out.<\/p>\n<p>Discussion from [1] led to commit a75ffa26122b (&#8220;memcg, oom: do not bypass<br \/>\noom killer for dying tasks&#8221;) which routes dying tasks into the OOM path<br \/>\nprecisely so the oom_reaper can reap their mm and free the memory<br \/>\nasynchronously.  But the reaper is best-effort and one-shot: if it cannot<br \/>\ntake mmap_lock for read (e.g.  a sibling thread holds it for write) it<br \/>\nsets MMF_OOM_SKIP and never retries, leaving only the glacial<br \/>\noom_lock-serialized synchronous drain.<\/p>\n<p>Once MMF_OOM_SKIP is set there is no more asynchronous reclaim coming for<br \/>\nthe mm, so a dying task charging against it has nothing left to wait for:<br \/>\nit frees its memory only once it finishes exiting.  Running reclaim and<br \/>\nthe (no-victim) OOM killer for it is then pointless, and doing it for 10s<br \/>\nof thousands of exiting threads is what serializes them behind oom_lock.<br \/>\nSo before reclaim, if current is an OOM victim whose reaper is done, fail<br \/>\nthe charge.<\/p>\n<p>Reproduced with 20k threads, each parking a robust futex head on its own<br \/>\nzswapped page, OOM-group-killed while a sibling holds mmap_lock for write<br \/>\nso the reaper gives up and sets MMF_OOM_SKIP.  Tested on next-20260728 and<br \/>\nbaseline show ~90 seconds exit time while with the patch the exit time<br \/>\nreduced to ~3 seconds.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>memcg: bypass the reclaim and oom killer for dying tasks once oom_reaper is done<\/p>\n<p>At Meta, we are seeing instances where an OOM kil<\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-28505","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/28505","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=28505"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=28505"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=28505"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}