From: Prakash Gupta <prakash.gupta@oss.qualcomm.com>
To: Johannes Weiner <hannes@cmpxchg.org>,
Michal Hocko <mhocko@kernel.org>,
Roman Gushchin <roman.gushchin@linux.dev>,
Shakeel Butt <shakeel.butt@linux.dev>,
Muchun Song <muchun.song@linux.dev>,
Andrew Morton <akpm@linux-foundation.org>,
Kefeng Wang <wangkefeng.wang@huawei.com>,
"Matthew Wilcox (Oracle)" <willy@infradead.org>
Cc: linux-arm-msm@vger.kernel.org, cgroups@vger.kernel.org,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org,
Prakash Gupta <prakash.gupta@oss.qualcomm.com>
Subject: [PATCH] mm/memcg: fix NULL nodeinfo[] dereference on late-onlined nodes
Date: Mon, 10 Aug 2026 11:16:30 +0530 [thread overview]
Message-ID: <20260810-memcg-nodeinfo-null-guard-v1-1-77a73204d63a@oss.qualcomm.com> (raw)
memcg->nodeinfo[] entries are allocated only for nodes present at
css_alloc time. When a node is onlined after a memcg is created its
nodeinfo[] slot remains NULL. Two call sites dereference these slots
unconditionally:
lruvec_stat_mod_folio() calls mem_cgroup_lruvec() which reads
memcg->nodeinfo[pgdat->node_id] without a NULL check. On a system
where a node is onlined after the memcg is created, any folio stat
update for that node crashes with a NULL pointer dereference:
Unable to handle kernel paging request at virtual address ffffffbebf7e1908
pc : lruvec_stat_mod_folio+0xf0/0x444 [6.18.21-android17-5]
lr : lruvec_stat_mod_folio+0x88/0x444
Call trace:
lruvec_stat_mod_folio+0xf0/0x444
folio_add_new_anon_rmap+0xac/0x2b8
do_wp_page+0x768/0xc80
handle_mm_fault+0x37c/0x8c4
do_page_fault+0x140/0xa1c
do_mem_abort+0x54/0x74
el0_da+0x48/0x8c
el0t_64_sync_handler+0x20/0x130
el0t_64_sync+0x1c4/0x1c8
__invalidate_reclaim_iterators() iterates for_each_node() and reads
from->nodeinfo[nid]->iter without checking for NULL. for_each_node()
visits all possible nodes, so this is reachable whenever a node is
onlined after the memcg was created.
Fix lruvec_stat_mod_folio() by checking nodeinfo[pgdat->node_id]
directly and falling back to mod_node_page_state() when NULL, mirroring
the existing !memcg early-return path. The fallback must not go through
mod_lruvec_state() since that calls mod_memcg_lruvec_state() which uses
container_of() to recover the mem_cgroup_per_node from the lruvec
pointer; passing &pgdat->__lruvec there produces a garbage pointer.
When nodeinfo[nid] is NULL the memcg has no per-node accounting
structure for that node, so node-level accounting is correct.
Fix __invalidate_reclaim_iterators() by skipping NULL nodeinfo[] slots.
Also switch lruvec_stat_mod_folio() to folio_memcg_check() which uses
READ_ONCE() to safely read folio->memcg_data in an unlocked context.
Fixes: 6c77b607ee26 ("mm: kill lock|unlock_page_memcg()")
Cc: stable@vger.kernel.org
Assisted-by: pi:claude-sonnet-4-5
Signed-off-by: Prakash Gupta <prakash.gupta@oss.qualcomm.com>
---
mm/memcontrol.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 6dc4888a90f3..2a6f02956b89 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -977,11 +977,12 @@ void lruvec_stat_mod_folio(struct folio *folio, enum node_stat_item idx,
int val)
{
struct mem_cgroup *memcg;
+ struct mem_cgroup_per_node *mz;
pg_data_t *pgdat = folio_pgdat(folio);
struct lruvec *lruvec;
rcu_read_lock();
- memcg = folio_memcg(folio);
+ memcg = folio_memcg_check(folio);
/* Untracked pages have no memcg, no lruvec. Update only the node */
if (!memcg) {
rcu_read_unlock();
@@ -989,7 +990,18 @@ void lruvec_stat_mod_folio(struct folio *folio, enum node_stat_item idx,
return;
}
- lruvec = mem_cgroup_lruvec(memcg, pgdat);
+ mz = memcg->nodeinfo[pgdat->node_id];
+ if (unlikely(!mz)) {
+ /*
+ * nodeinfo[nid] is NULL when a node is onlined after the
+ * memcg was created. Fall back to node-level accounting.
+ */
+ rcu_read_unlock();
+ mod_node_page_state(pgdat, idx, val);
+ return;
+ }
+
+ lruvec = &mz->lruvec;
mod_lruvec_state(lruvec, idx, val);
rcu_read_unlock();
}
@@ -1320,6 +1332,8 @@ static void __invalidate_reclaim_iterators(struct mem_cgroup *from,
for_each_node(nid) {
mz = from->nodeinfo[nid];
+ if (!mz)
+ continue;
iter = &mz->iter;
cmpxchg(&iter->position, dead_memcg, NULL);
}
---
base-commit: 075b74841bd0065a3bda3440873c747938e69b68
change-id: 20260808-memcg-nodeinfo-null-guard-dce5f63a2349
Best regards,
--
Prakash Gupta <prakash.gupta@oss.qualcomm.com>
next reply other threads:[~2026-08-10 5:46 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 5:46 Prakash Gupta [this message]
2026-08-10 7:58 ` Muchun Song
2026-08-11 5:16 ` Prakash Gupta
2026-08-11 6:19 ` Muchun Song
2026-08-26 6:30 ` kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260810-memcg-nodeinfo-null-guard-v1-1-77a73204d63a@oss.qualcomm.com \
--to=prakash.gupta@oss.qualcomm.com \
--cc=akpm@linux-foundation.org \
--cc=cgroups@vger.kernel.org \
--cc=hannes@cmpxchg.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mhocko@kernel.org \
--cc=muchun.song@linux.dev \
--cc=roman.gushchin@linux.dev \
--cc=shakeel.butt@linux.dev \
--cc=stable@vger.kernel.org \
--cc=wangkefeng.wang@huawei.com \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®