Skip to contentSkip to conversation

slab: reset obj_ext when it is not actually valid during freeing

PATCH Applied Hao Ge 4 participants 8 messages Oct 15, 2025 – Oct 16, 2025

Subsystems (2)
lore
Share & tools
lore

Hao Ge

author
#0 lore raw

PATCH v5 1/1 slab: reset obj_ext when it is not actually valid during freeing

From: Hao Ge <[email protected]>

If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we did 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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

When freeing a slab, we clear slab->obj_exts and reset it to 0 if the obj_ext array has been successfully allocated. So let's reset slab->obj_exts to 0 when freeing a slab if the obj_ext array allocated fail to allow them to be returned to the buddy system more smoothly.

---
v5: Adopt the simpler solution proposed by Vlastimil;
Many thanks to him

---
 mm/slub.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
mm/slub.c +9 -1
index b1f15598fbfd..2e4340c75be2 100644
@@ -2170,8 +2170,16 @@ 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 will end up here.
+		 * Therefore, we should clear the OBJEXTS_ALLOC_FAIL flag first when freeing a slab.
+		 * Then let's set it to 0 as below.
+		 */
+		slab->obj_exts = 0;
 		return;
+	}
 
 	/*
 	 * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its
Signature · 1 line
2.25.1
#1 lore raw
Quoted 33 lines · Hao Ge

From: Hao Ge <[email protected]>

If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we did 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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

When freeing a slab, we clear slab->obj_exts and reset it to 0 if the obj_ext array has been successfully allocated. So let's reset slab->obj_exts to 0 when freeing a slab if the obj_ext array allocated fail to allow them to be returned to the buddy system more smoothly.

---
v5: Adopt the simpler solution proposed by Vlastimil;
Many thanks to him

---
 mm/slub.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
mm/slub.c +0 -1
index b1f15598fbfd..2e4340c75be2 100644
@@ -2170,8 +2170,16 @@ static inline void free_slab_obj_exts(struct slab *slab)
        struct slabobj_ext *obj_exts;
 
        obj_exts = slab_obj_exts(slab);
-       if (!obj_exts)

mm/slub.c static inline void free_slab_obj_exts(struct slab *slab) · line 2182

Quoted 10 lines
mm/slub.c +9 -0
+       if (!obj_exts) {
+               /*
+                * If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL,
+                * In this case, we will end up here.
+                * Therefore, we should clear the OBJEXTS_ALLOC_FAIL flag first when freeing a slab.
+                * Then let's set it to 0 as below.
+                */
+               slab->obj_exts = 0;
                return;
+       }

How about this instead:

static inline void free_slab_obj_exts(struct slab *slab)

{
        struct slabobj_ext *obj_exts;

        obj_exts = slab_obj_exts(slab);
+        /*
+         * Reset obj_exts to ensure all bits including OBJEXTS_ALLOC_FAIL
+         * are always cleared.
+         */
+        slab->obj_exts = 0;
        if (!obj_exts)
                return;

        /*
         * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its
         * corresponding extension will be NULL. alloc_tag_sub() will throw a
         * warning if slab has extensions but the extension of an object is
         * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that
         * the extension for obj_exts is expected to be NULL.
         */
        mark_objexts_empty(obj_exts);
        kfree(obj_exts);

- slab->obj_exts = 0;
}

Quoted 6 lines
mm/slub.c +0 -1
        /*
         * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its
--

2.25.1

#2 lore raw
Quoted 60 lines · Suren Baghdasaryan
Quoted 33 lines · Hao Ge

From: Hao Ge <[email protected]>

If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we did 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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

When freeing a slab, we clear slab->obj_exts and reset it to 0 if the obj_ext array has been successfully allocated. So let's reset slab->obj_exts to 0 when freeing a slab if the obj_ext array allocated fail to allow them to be returned to the buddy system more smoothly.

---
v5: Adopt the simpler solution proposed by Vlastimil;
Many thanks to him

---
 mm/slub.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
mm/slub.c +0 -1
index b1f15598fbfd..2e4340c75be2 100644
@@ -2170,8 +2170,16 @@ static inline void free_slab_obj_exts(struct slab *slab)
        struct slabobj_ext *obj_exts;
 
        obj_exts = slab_obj_exts(slab);
-       if (!obj_exts)

mm/slub.c static inline void free_slab_obj_exts(struct slab *slab) · line 2182

Quoted 10 lines
mm/slub.c +9 -0
+       if (!obj_exts) {
+               /*
+                * If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL,
+                * In this case, we will end up here.
+                * Therefore, we should clear the OBJEXTS_ALLOC_FAIL flag first when freeing a slab.
+                * Then let's set it to 0 as below.
+                */
+               slab->obj_exts = 0;
                return;
+       }

How about this instead:

static inline void free_slab_obj_exts(struct slab *slab)

{
        struct slabobj_ext *obj_exts;

        obj_exts = slab_obj_exts(slab);
(quoted diff) +5 -0
+        /*
+         * Reset obj_exts to ensure all bits including OBJEXTS_ALLOC_FAIL
+         * are always cleared.
+         */
+        slab->obj_exts = 0;
        if (!obj_exts)
                return;
 

Review

Quoted 10 lines
(quoted diff) +0 -1
        /*
         * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its
         * corresponding extension will be NULL. alloc_tag_sub() will throw a
         * warning if slab has extensions but the extension of an object is
         * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that
         * the extension for obj_exts is expected to be NULL.
         */
        mark_objexts_empty(obj_exts);
        kfree(obj_exts);
-        slab->obj_exts = 0;

You have an older base, check current mainline, we evaluate slab->obj_exts later in the function

Quoted 8 lines

}

Quoted 6 lines
mm/slub.c +0 -1
        /*
         * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its
--

2.25.1

#3 lore raw
Quoted 73 lines · Vlastimil Babka
Quoted 60 lines · Suren Baghdasaryan
Quoted 33 lines · Hao Ge

From: Hao Ge <[email protected]>

If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we did 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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

When freeing a slab, we clear slab->obj_exts and reset it to 0 if the obj_ext array has been successfully allocated. So let's reset slab->obj_exts to 0 when freeing a slab if the obj_ext array allocated fail to allow them to be returned to the buddy system more smoothly.

---
v5: Adopt the simpler solution proposed by Vlastimil;
Many thanks to him

---
 mm/slub.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
mm/slub.c +0 -1
index b1f15598fbfd..2e4340c75be2 100644
@@ -2170,8 +2170,16 @@ static inline void free_slab_obj_exts(struct slab *slab)
        struct slabobj_ext *obj_exts;
 
        obj_exts = slab_obj_exts(slab);
-       if (!obj_exts)

mm/slub.c static inline void free_slab_obj_exts(struct slab *slab) · line 2182

Quoted 10 lines
mm/slub.c +9 -0
+       if (!obj_exts) {
+               /*
+                * If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL,
+                * In this case, we will end up here.
+                * Therefore, we should clear the OBJEXTS_ALLOC_FAIL flag first when freeing a slab.
+                * Then let's set it to 0 as below.
+                */
+               slab->obj_exts = 0;
                return;
+       }

How about this instead:

static inline void free_slab_obj_exts(struct slab *slab)

{
        struct slabobj_ext *obj_exts;

        obj_exts = slab_obj_exts(slab);
(quoted diff) +5 -0
+        /*
+         * Reset obj_exts to ensure all bits including OBJEXTS_ALLOC_FAIL
+         * are always cleared.
+         */
+        slab->obj_exts = 0;
        if (!obj_exts)
                return;
 

Review

Quoted 10 lines
(quoted diff) +0 -1
        /*
         * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its
         * corresponding extension will be NULL. alloc_tag_sub() will throw a
         * warning if slab has extensions but the extension of an object is
         * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that
         * the extension for obj_exts is expected to be NULL.
         */
        mark_objexts_empty(obj_exts);
        kfree(obj_exts);
-        slab->obj_exts = 0;
Quoted 2 lines

You have an older base, check current mainline, we evaluate slab->obj_exts later in the function

Ah, sorry about that. Yeah, then this looks good.

Quoted 10 lines
Quoted 9 lines

}

Quoted 6 lines
mm/slub.c +0 -1
        /*
         * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its
--

2.25.1

#4 lore raw
In reply to Suren Baghdasaryan
Quoted 17 lines · Suren Baghdasaryan
Quoted 16 lines · Hao Ge

From: Hao Ge <[email protected]>

If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we did 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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

When freeing a slab, we clear slab->obj_exts and reset it to 0 if the obj_ext array has been successfully allocated. So let's reset slab->obj_exts to 0 when freeing a slab if the obj_ext array allocated fail to allow them to be returned to the buddy system more smoothly.

Quoted 4 lines
Quoted 4 lines

---
v5: Adopt the simpler solution proposed by Vlastimil;
Many thanks to him

I've massaged the commit log and comments a bit and also realized that AFAICS we're actually fixing an issue that predates 7612833192d5 ("slab:
Reuse first bit for OBJEXTS_ALLOC_FAIL"). Am I wrong?

----8<----
From 8151384e5baf34db5812ed51e2e463796ab6e973 Mon Sep 17 00:00:00 2001
From: Hao Ge <[email protected]>
Date: Wed, 15 Oct 2025 22:16:42 +0800
Subject: [PATCH] slab: reset slab->obj_ext when freeing and it is
OBJEXTS_ALLOC_FAIL

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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

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")
---
 mm/slub.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
mm/slub.c +8 -1
index 13ae4491136a..a8fcc7e6f25a 100644
@@ -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
Signature · 1 line
2.51.0
Reviewed-by
#5 lore raw
Quoted 51 lines · Vlastimil Babka
Quoted 17 lines · Suren Baghdasaryan
Quoted 16 lines · Hao Ge

From: Hao Ge <[email protected]>

If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we did 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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

When freeing a slab, we clear slab->obj_exts and reset it to 0 if the obj_ext array has been successfully allocated. So let's reset slab->obj_exts to 0 when freeing a slab if the obj_ext array allocated fail to allow them to be returned to the buddy system more smoothly.

Quoted 4 lines
Quoted 4 lines

---
v5: Adopt the simpler solution proposed by Vlastimil;
Many thanks to him

I've massaged the commit log and comments a bit and also realized that AFAICS we're actually fixing an issue that predates 7612833192d5 ("slab:
Reuse first bit for OBJEXTS_ALLOC_FAIL"). Am I wrong?

----8<----
From 8151384e5baf34db5812ed51e2e463796ab6e973 Mon Sep 17 00:00:00 2001
From: Hao Ge <[email protected]>
Date: Wed, 15 Oct 2025 22:16:42 +0800
Subject: [PATCH] slab: reset slab->obj_ext when freeing and it is
OBJEXTS_ALLOC_FAIL

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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

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.

Quoted 5 lines
Fixes 09c46563ff6d ("codetag: debug: introduce OBJEXTS_ALLOC_FAIL to mark failed slab_ext allocations")
Quoted 29 lines
---
 mm/slub.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
mm/slub.c +8 -2
index 13ae4491136a..a8fcc7e6f25a 100644
@@ -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
--

2.51.0

Reviewed-by
#6 lore raw
In reply to Vlastimil Babka
Quoted 18 lines · Vlastimil Babka
Quoted 17 lines · Suren Baghdasaryan
Quoted 16 lines · Hao Ge

From: Hao Ge <[email protected]>

If obj_exts allocation failed, slab->obj_exts is set to OBJEXTS_ALLOC_FAIL, But we did 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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

When freeing a slab, we clear slab->obj_exts and reset it to 0 if the obj_ext array has been successfully allocated. So let's reset slab->obj_exts to 0 when freeing a slab if the obj_ext array allocated fail to allow them to be returned to the buddy system more smoothly.

Quoted 8 lines
Quoted 4 lines
Quoted 4 lines

---
v5: Adopt the simpler solution proposed by Vlastimil;
Many thanks to him

I've massaged the commit log and comments a bit and also realized that AFAICS we're actually fixing an issue that predates 7612833192d5 ("slab:
Reuse first bit for OBJEXTS_ALLOC_FAIL"). Am I wrong?

You're not wrong!

Quoted 7 lines

----8<----
From 8151384e5baf34db5812ed51e2e463796ab6e973 Mon Sep 17 00:00:00 2001
From: Hao Ge <[email protected]>
Date: Wed, 15 Oct 2025 22:16:42 +0800
Subject: [PATCH] slab: reset slab->obj_ext when freeing and it is
OBJEXTS_ALLOC_FAIL

Quoted 8 lines

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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

nit: maybe this can be massaged as "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."

Quoted 9 lines

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.

Quoted 6 lines
Fixes 09c46563ff6d ("codetag: debug: introduce OBJEXTS_ALLOC_FAIL to mark failed slab_ext allocations")

---

Looks good to me,

Signature · 2 lines
Cheers,
Harry / Hyeonggon
Quoted 26 lines
 mm/slub.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
mm/slub.c +8 -1
index 13ae4491136a..a8fcc7e6f25a 100644
@@ -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
Signature · 1 line
2.51.0
#7 lore raw
Quoted 16 lines · Harry Yoo
Quoted 7 lines

----8<----
From 8151384e5baf34db5812ed51e2e463796ab6e973 Mon Sep 17 00:00:00 2001
From: Hao Ge <[email protected]>
Date: Wed, 15 Oct 2025 22:16:42 +0800
Subject: [PATCH] slab: reset slab->obj_ext when freeing and it is
OBJEXTS_ALLOC_FAIL

Quoted 8 lines

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 it was mistakenly assumed that a valid folio->memcg_data was not cleared before freeing the folio.

Quoted 5 lines

nit: maybe this can be massaged as "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."

Thanks, applied

Quoted 16 lines
Quoted 9 lines

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.

Quoted 6 lines
Fixes 09c46563ff6d ("codetag: debug: introduce OBJEXTS_ALLOC_FAIL to mark failed slab_ext allocations")

---

Quoted 2 lines

Looks good to me,

and this too.