[PATCH 1/1] mm/shrinker: add NULL checks after rcu_dereference() in shrinker bit functions
From: fffsqian
Date: Wed Jun 24 2026 - 05:56:30 EST
From: Qingshuang Fu <fuqingshuang@xxxxxxxxxx>
The functions set_shrinker_bit(), xchg_nr_deferred_memcg(), and
add_nr_deferred_memcg() access shrinker_info fields immediately
after rcu_dereference() without checking for NULL.
This is inconsistent with shrink_slab_memcg() which properly checks
"if (unlikely(!info)) goto unlock;" before accessing info fields.
The shrinker_info can be NULL during memcg initialization or after
shrinker_info expansion failure. Directly accessing info->map_nr_max
or info->unit[] without NULL validation could cause kernel NULL
pointer dereference and panic.
Fix this by adding proper NULL checks in all three functions to
ensure consistent RCU protection and prevent potential crashes in
the shrinker subsystem.
Fixes: 307bececcd1205bcb ("mm: shrinker: add a secondary array for shrinker_info::{map, nr_deferred}")
Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: Dave Chinner <david@xxxxxxxxxxxxx>
Cc: Qi Zheng <qi.zheng@xxxxxxxxx>
Cc: Roman Gushchin <roman.gushchin@xxxxxxxxx>
Cc: Muchun Song <muchun.song@xxxxxxxxx>
Cc: linux-mm@xxxxxxxxx
Signed-off-by: Qingshuang Fu <fuqingshuang@xxxxxxxxxx>
---
mm/shrinker.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/mm/shrinker.c b/mm/shrinker.c
index 7082d01c8c9d..ecde3cc44459 100644
--- a/mm/shrinker.c
+++ b/mm/shrinker.c
@@ -200,6 +200,8 @@ void set_shrinker_bit(struct mem_cgroup *memcg, int nid, int shrinker_id)
rcu_read_lock();
info = rcu_dereference(memcg->nodeinfo[nid]->shrinker_info);
+ if (unlikely(!info))
+ goto unlock;
if (!WARN_ON_ONCE(shrinker_id >= info->map_nr_max)) {
struct shrinker_info_unit *unit;
@@ -208,6 +210,7 @@ void set_shrinker_bit(struct mem_cgroup *memcg, int nid, int shrinker_id)
smp_mb__before_atomic();
set_bit(shrinker_id_to_offset(shrinker_id), unit->map);
}
+unlock:
rcu_read_unlock();
}
}
@@ -258,6 +261,10 @@ static long xchg_nr_deferred_memcg(int nid, struct shrinker *shrinker,
rcu_read_lock();
info = rcu_dereference(memcg->nodeinfo[nid]->shrinker_info);
+ if (unlikely(!info)) {
+ rcu_read_unlock();
+ return 0;
+ }
unit = info->unit[shrinker_id_to_index(shrinker->id)];
nr_deferred = atomic_long_xchg(&unit->nr_deferred[shrinker_id_to_offset(shrinker->id)], 0);
rcu_read_unlock();
@@ -274,6 +281,10 @@ static long add_nr_deferred_memcg(long nr, int nid, struct shrinker *shrinker,
rcu_read_lock();
info = rcu_dereference(memcg->nodeinfo[nid]->shrinker_info);
+ if (unlikely(!info)) {
+ rcu_read_unlock();
+ return 0;
+ }
unit = info->unit[shrinker_id_to_index(shrinker->id)];
nr_deferred =
atomic_long_add_return(nr, &unit->nr_deferred[shrinker_id_to_offset(shrinker->id)]);
base-commit: 840ef6c78e6a2f694b578ecb9063241c992aaa9e
--
2.25.1