CVE-2026-98225: mm/shrinker: fix bogus set_shrinker_bit() with cgroup.memory=nokmem
In the Linux kernel, the following vulnerability has been resolved:
mm/shrinker: fix bogus setshrinkerbit() with cgroup.memory=nokmem
With cgroup.memory=nokmem, shrinkermemcgalloc() bails out early and never allocates an id, so shrinker->id keeps the 0 it got from the kzalloc() in shrinkeralloc(). listlruinit() then copies that 0 into lru->shrinkerid, where it looks like a valid bit index.
Nothing calls expandshrinkerinfo() on nokmem either, so shrinkernrmax stays 0 and every memcg ends up with an empty map (mapnrmax == 0).
deferredsplitfolio() hands a real memcg to listlruadd() regardless of whether the lru is memcg aware, so the first THP queued in a cgroup does setshrinkerbit(memcg, nid, 0) and trips the bounds check:
WARNING: mm/shrinker.c:212 at setshrinkerbit+0x7d/0x90, CPU#126 Call Trace: <TASK> deferredsplitfolio+0x18c/0x220 mapanonfoliopmdnopf+0xdd/0x130 mapanonfoliopmdpf+0x14/0xb0 dohugepmdanonymouspage+0x1a1/0x620 handlemmfault+0xea9/0x10d0 handlemmfault+0xe5/0x320 douseraddrfault+0x1cc/0x870 excpagefault+0x81/0x1b0 asmexcpagefault+0x27/0x30 </TASK>
Harmless, the WARNONONCE() is what keeps the out of bounds unit[] read from happening, but the id should not look valid in the first place. Clear it before returning.
Two other spots could paper over this: drop the id in listlruinit() when nokmem turns memcgaware off, or make deferredsplitfolio() pass NULL like listlruaddobj() does. Both leave shrinker->id lying around for the next caller, so fix it where the id is handed out.