CVE-2026-93197: memcg: move LRU size accounting on reparenting instead of copying it

Published Sep 17, 2026
·
Updated

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

memcg: move LRU size accounting on reparenting instead of copying it

When a memory cgroup is offlined its LRU folios are reparented to the parent. lruvecreparentlru() splices the child's lists into the parent's and credits the parent with the child's per-zone lruzonesize[], but never clears the child's copy, so the size is copied rather than moved. lrugenreparentmemcg() does the same for MGLRU.

The parent is left correct, credited with exactly the folios it took over. The stale value sits on the child and nothing will correct it: folio->memcgdata now resolves to the parent, so every later updatelrusize() for those folios goes there.

Dying cgroups are not freed immediately and memcgroupiter() still walks them, so shrinklruvec() keeps being called on them. getscancount() reads the phantom counter through lruveclrusize() and the scan loop then grinds through nr[] in SWAPCLUSTERMAX steps against an empty list, for as long as the dead cgroup lives. Under MGLRU the MGLRU scanner runs instead, but countshadownodes() sums all of NRLRULISTS through lruveclrusize() and over-budgets the shadow node limit just the same.

On one 251 GiB host a sweep of every mz->lruzonesize[] found 380 counters describing folios on no list at all: 124777314 pages, 476 GiB, 1.89x the machine's RAM, across 57 cgroups. All were on memcgs with CSSDYING set and CSSONLINE clear, and parent/child pairs reported byte-identical sizes.

LRUUNEVICTABLE needs its size moved too. Its list is deliberately not spliced because lruvecinit() poisons the head - the unevictable LRU is imaginary and folios are never threaded on it - but the size is kept by lruvecaddfolio()/lruvecdelfolio() and those folios account to the parent from here on.

This depends on commit bf4ade7dbd76 ("memcg: keep folio's objcg same as its node") and must not be backported ahead of it. Without that invariant a folio's objcg can belong to another node, so a folio already spliced onto the parent's list can still resolve to the child's lruvec until the objcg's node is reparented in a later iteration of memcgreparentobjcgs(); clearing the child's counter early then lets lruvecdelfolio() underflow it and trip the WARNONCE()/VMBUGON() in memcgroupupdatelrusize().

Affected Software

1 affected component
Linux Linux kernel

Event History

Sep 17, 2026
CVE Published
via MITRE·04:12 PM
Data Sourced
via MITRE·04:12 PM
Description

Frequently Asked Questions

1

When does the excessive reclaim scanning occur?

It occurs after a memory cgroup is offlined and its LRU folios are reparented to its parent. The offlined cgroup retains stale LRU size accounting while it remains in the dying state and is still visited by memory-cgroup iteration.

2

What is the observable effect of the stale accounting?

The reclaim path can calculate scan counts from phantom LRU entries and repeatedly scan an empty LRU list in SWAP_CLUSTER_MAX-sized steps for the lifetime of the dead cgroup. With MGLRU, the scanner can also over-budget shadow-node limits because it includes the stale LRU sizes.

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