]> www.infradead.org Git - users/hch/misc.git/commitdiff
slab: reset slab->obj_ext when freeing and it is OBJEXTS_ALLOC_FAIL
authorHao Ge <gehao@kylinos.cn>
Wed, 15 Oct 2025 14:16:42 +0000 (22:16 +0800)
committerVlastimil Babka <vbabka@suse.cz>
Thu, 16 Oct 2025 13:16:45 +0000 (15:16 +0200)
If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL,
But we do not clear it when freeing the slab. Since OBJEXTS_ALLOC_FAIL and
MEMCG_DATA_OBJEXTS currently share the same bit position, during the
release of the associated folio, a VM_BUG_ON_FOLIO() check in
folio_memcg_kmem() is triggered because the OBJEXTS_ALLOC_FAIL flag was
not cleared, causing it to be interpreted as a kmem folio (non-slab)
with MEMCG_OBJEXTS_DATA flag set, which is invalid because
MEMCG_OBJEXTS_DATA is supposed to be set only on slabs.

Another problem that predates sharing the OBJEXTS_ALLOC_FAIL and
MEMCG_DATA_OBJEXTS bits is that on configurations with
is_check_pages_enabled(), the non-cleared bit in page->memcg_data will
trigger a free_page_is_bad() failure "page still charged to cgroup"

When freeing a slab, we clear slab->obj_exts if the obj_ext array has
been successfully allocated. So let's clear it also when the allocation
has failed.

Fixes: 09c46563ff6d ("codetag: debug: introduce OBJEXTS_ALLOC_FAIL to mark failed slab_ext allocations")
Fixes: 7612833192d5 ("slab: Reuse first bit for OBJEXTS_ALLOC_FAIL")
Link: https://lore.kernel.org/all/20251015141642.700170-1-hao.ge@linux.dev/
Cc: <stable@vger.kernel.org>
Signed-off-by: Hao Ge <gehao@kylinos.cn>
Reviewed-by: Suren Baghdasaryan <surenb@google.com>
Reviewed-by: Harry Yoo <harry.yoo@oracle.com>
Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
mm/slub.c

index 13ae4491136ac6de78402bb3e08bedf012bb89f8..a8fcc7e6f25a94d07ec3a4574f9f681f6e9d5864 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2170,8 +2170,15 @@ static inline void free_slab_obj_exts(struct slab *slab)
        struct slabobj_ext *obj_exts;
 
        obj_exts = slab_obj_exts(slab);
-       if (!obj_exts)
+       if (!obj_exts) {
+               /*
+                * If obj_exts allocation failed, slab->obj_exts is set to
+                * OBJEXTS_ALLOC_FAIL. In this case, we end up here and should
+                * clear the flag.
+                */
+               slab->obj_exts = 0;
                return;
+       }
 
        /*
         * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its