diff mbox series

[7/9] mm: kmemleak: erase page->s_mem in slab_destroy

Message ID 20230123170419.7292-8-george@enfabrica.net (mailing list archive)
State New
Headers show
Series mm: kmemleak: fix unreported memory leaks | expand

Commit Message

George Prekas Jan. 23, 2023, 5:04 p.m. UTC
The field s_mem of struct page is initialized with the virtual address
of the page in function alloc_slabmgmt. If kmalloc allocates an object
that starts on this page, then kmemleak knows that this object has 2
references. On slab_destroy, s_mem should not continue referring to any
allocated object in the future.

Specifically, assume that initially the 4KB cache uses page[5] and its
s_mem = 0x5000. Then assume that this cache releases page[5] and the 8KB
cache allocates page[4] and page[5]. Subsequently, kmalloc returns an
8KB object at address 0x4000 which will have 3 references: the returned
pointer from kmalloc, page[4].s_mem = 0x4000, and page[5].s_mem. This
object can leak without detection.

Signed-off-by: George Prekas <george@enfabrica.net>
---
 mm/slab.c | 3 +++
 1 file changed, 3 insertions(+)

Comments

Christoph Lameter Jan. 26, 2023, 11:28 a.m. UTC | #1
On Mon, 23 Jan 2023, George Prekas wrote:

> The field s_mem of struct page is initialized with the virtual address
> of the page in function alloc_slabmgmt. If kmalloc allocates an object
> that starts on this page, then kmemleak knows that this object has 2
> references. On slab_destroy, s_mem should not continue referring to any
> allocated object in the future.

Nope.

s_mem is a pointer to an array of objects. It is not a pointer to a page.

If a slab-caches is used for slabmanagement then these objects
contain slab metadata which may be a bit confusing.
diff mbox series

Patch

diff --git a/mm/slab.c b/mm/slab.c
index a927e1a285d1..aa5eb725ee9c 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -1611,6 +1611,9 @@  static void slab_destroy(struct kmem_cache *cachep, struct slab *slab)
 {
 	void *freelist;
 
+	/* Erase the page's virtual address from s_mem */
+	kmemleak_erase(&slab->s_mem);
+
 	freelist = slab->freelist;
 	slab_destroy_debugcheck(cachep, slab);
 	if (unlikely(cachep->flags & SLAB_TYPESAFE_BY_RCU))