CVE-2026-93241: memcg: bypass the reclaim and oom killer for dying tasks once oom_reaper is done

Published Sep 24, 2026
·
Updated

In the Linux kernel, the following vulnerability has been resolved:

memcg: bypass the reclaim and oom killer for dying tasks once oomreaper is done

At Meta, we are seeing instances where an OOM killed job is stuck in the exit path for several hours. In one particular case, the job was stuck for more than 8 hours and I had to manually remove the memory.max limits to allow the process to exit.

The job was a single process job and had ~55 GiB memory.max and zswap enabled. It had almost 0 anon in memory and ~111 GiB in zswap compressed to ~51 GiB zswap pool (i.e. almost all of memory.current was zswap). Nothing was left on the LRUs to reclaim.

On further inspection, I observed ~20k threads of that process stuck with the following stack:

[<0>] memcgroupoutofmemory+0x4e/0xa0 [<0>] chargememcg+0x8bf/0x990 [<0>] memcgroupswapinchargefolio+0x4e/0x80 [<0>] readswapcacheasync+0x10c/0x260 [<0>] swapinreadahead+0x116/0x3f0 [<0>] doswappage+0x13c/0x1ce0 [<0>] handlemmfault+0x61d/0x11f0 [<0>] douseraddrfault+0x3e7/0x6d0 [<0>] excpagefault+0x8f/0x110 [<0>] asmexcpagefault+0x22/0x30 [<0>] getuser8+0x14/0x20 [<0>] futexcleanup+0x27/0x1c0 [<0>] futexexitrelease+0x47/0x60 [<0>] doexit+0x107/0x940 [<0>] dogroupexit+0x81/0xa0 [<0>] getsignal+0x2b1/0x6e0 [<0>] archdosignalorrestart+0x1a/0x1c0 [<0>] exittousermodeloop+0xa8/0x1c0 [<0>] dosyscall64+0x152/0x250 [<0>] entrySYSCALL64afterhwframe+0x4b/0x53

In addition the dmesg was filled with "Out of memory and no killable processes..." messages.

I have no idea why oom reaper was not able to reap/unmap the process. My guess is that since oom reaper tries to acquire mmaplock in read mode limited number of times and then gives up, there might be a thread of that process which had mmaplock in write mode at that time.

My initial suspicion was the futexcleanup and kernel page fault causing infinite fault and charge retries but that was put to rest in previous discussions happened on similar problem [1].

My current theory is that it is just a simple slow serialization behind the oomlock. Unlike page allocator, memcg charge code takes the oomlock without the "try". Though memcg oom code uses mutexlockkillable(), note that in the call stack getsignal() consumes SIGKILL (or sigdelset(SIGKILL)) before calling dogroupexit(). So this mutexlockkillable() is just a mutexlock() here. Therefore 10s of thousands of threads are waiting on oomlock and one by one they get -EFAULT from getuser() in the futex cleanup code and bails out.

Discussion from [1] led to commit a75ffa26122b ("memcg, oom: do not bypass oom killer for dying tasks") which routes dying tasks into the OOM path precisely so the oomreaper can reap their mm and free the memory asynchronously. But the reaper is best-effort and one-shot: if it cannot take mmaplock for read (e.g. a sibling thread holds it for write) it sets MMFOOMSKIP and never retries, leaving only the glacial oomlock-serialized synchronous drain.

Once MMFOOMSKIP is set there is no more asynchronous reclaim coming for the mm, so a dying task charging against it has nothing left to wait for: it frees its memory only once it finishes exiting. Running reclaim and the (no-victim) OOM killer for it is then pointless, and doing it for 10s of thousands of exiting threads is what serializes them behind oomlock. So before reclaim, if current is an OOM victim whose reaper is done, fail the charge.

Reproduced with 20k threads, each parking a robust futex head on its own zswapped page, OOM-group-killed while a sibling holds mmaplock for write so the reaper gives up and sets MMFOOMSKIP. Tested on next-20260728 and baseline show ~90 seconds exit time while with the patch the exit time reduced to ~3 seconds.

Affected Software

1 affected component
Linux Linux kernel

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade Linux kernel to a version that resolves this vulnerability.

    Patch a75ffa26122b

Event History

Sep 24, 2026
CVE Published
via MITRE·03:34 PM
Data Sourced
via MITRE·03:34 PM
Description
Data Sourced
via NVD·04:17 PM
Description

Frequently Asked Questions

1

Which workloads are most likely to encounter this condition?

The reported case involved a single-process job that had been OOM killed while constrained by memory.max, with zswap enabled and nearly all of its memory accounted for by compressed zswap pages. There was little or no anonymous memory remaining on reclaimable LRUs.

2

How can an affected system be recognized?

An OOM-killed workload may remain stuck in its exit path for hours after oom_reaper has completed. Many threads can be blocked in swap-in and memcg charging paths, including mem_cgroup_out_of_memory, charge_memcg, mem_cgroup_swapin_charge_folio, and futex cleanup during exit.

3

What can be done if the workload cannot exit and the fix is not yet available?

In the reported incident, manually removing the memory.max limit allowed the process to exit. This should be considered when an OOM-killed job is stuck and reclaimable memory is unavailable.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203