Re: [PATCH v3 01/15] mm/mempolicy: charge shared policy nodes
From: David Hildenbrand (Arm)
Date: Wed Oct 28 2026 - 06:08:04 EST
On 10/28/26 11:02, Balbir Singh wrote:
> On Wed, Oct 28, 2026 at 10:54:59AM +0100, David Hildenbrand (Arm) wrote:
>> From: Chris Mason <mason@xxxxxxxxxx>
>>
>> Any local user can bypass memory.max with mbind() on a tmpfs file, and
>> allocate unbounded kernel memory.
>>
>> mbind() sets a NUMA memory policy for a range of a mapping. If the
>> mapping is of a shmem and friends, the kernel also stores the policy on
>> the inode, in a tree with one entry per range of the file.
>>
>> Each mbind() on a single page of a mapping can add one shared policy node
>> (struct sp_node) and one struct mempolicy to that tree. They outlive the
>> mapping and stay around until the inode is destroyed. The tree is not
>> bounded by the file size, so the file can be empty.
>>
>> Memory that user space can allocate must be charged to a memory cgroup. A
>> slab cache does that for all of its objects if it is created with
>> SLAB_ACCOUNT. numa_policy_init() creates the slab caches for both
>> structs without SLAB_ACCOUNT, so they are not charged to any cgroup.
>>
>> Reproduced with mbind() on single pages of an empty tmpfs file, from a
>> cgroup with memory.max=8M: the global OOM killer killed tasks owned by
>> root, and the machine panicked.
>>
>> Fix this by creating both caches with SLAB_ACCOUNT. This change implies
>> that mbind() can now fail when running into memcg limits. Further,
>> the policy_cache also serves task and VMA policies, so they are
>> similarly accounted now.
>>
>> Note that per-file limits are not really helpful, as the user could
>> simply use many files; having limits per user is tricky as well.
>>
>> Fix generated by an LLM and submitted by Chris Mason in private.
>>
>> Fixes: 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg")
>> Cc: stable@xxxxxxxxxxxxxxx
>> Signed-off-by: Chris Mason <mason@xxxxxxxxxx>
>> Acked-by: Pedro Falcato <pfalcato@xxxxxxx>
>> Acked-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
>> Acked-by: Zi Yan <ziy@xxxxxxxxxx>
>> Reviewed-by: Baolin Wang <baolin.wang@xxxxxxxxxxxxxxxxx>
>> Tested-by: Gregory Price (Meta) <gourry@xxxxxxxxxx>
>> [ david: smaller description tweaks ]
>> Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
>> ---
>> mm/mempolicy.c | 4 ++--
>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/mm/mempolicy.c b/mm/mempolicy.c
>> index 0884c629670d..4da1cb8777f6 100644
>> --- a/mm/mempolicy.c
>> +++ b/mm/mempolicy.c
>> @@ -3372,11 +3372,11 @@ void __init numa_policy_init(void)
>>
>> policy_cache = kmem_cache_create("numa_policy",
>> sizeof(struct mempolicy),
>> - 0, SLAB_PANIC, NULL);
>> + 0, SLAB_PANIC | SLAB_ACCOUNT, NULL);
>>
>> sn_cache = kmem_cache_create("shared_policy_node",
>> sizeof(struct sp_node),
>> - 0, SLAB_PANIC, NULL);
>> + 0, SLAB_PANIC | SLAB_ACCOUNT, NULL);
>>
>> for_each_node(nid) {
>> preferred_node_policy[nid] = (struct mempolicy) {
>>
>
> You have many of them already, but anyway
>
> Reviewed-by: Balbir Singh <balbirs@xxxxxxxxxx>
I love it, thanks to all of you :)
--
Cheers,
David