From: Harry Yoo <harry.yoo@oracle.com>
To: syzbot <syzbot+cae7809e9dc1459e4e63@syzkaller.appspotmail.com>
Cc: akpm@linux-foundation.org, catalin.marinas@arm.com,
chao@kernel.org, hao.li@linux.dev, jaegeuk@kernel.org,
jannh@google.com, liam.howlett@oracle.com, linkinjeon@kernel.org,
linux-f2fs-devel@lists.sourceforge.net,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-mm@kvack.org, lorenzo.stoakes@oracle.com, pfalcato@suse.de,
sj1557.seo@samsung.com, syzkaller-bugs@googlegroups.com,
vbabka@kernel.org, vbabka@suse.cz, wangqing7171@gmail.com
Subject: Re: [syzbot] [mm?] [f2fs?] [exfat?] memory leak in __kfree_rcu_sheaf
Date: Wed, 18 Mar 2026 13:10:48 +0900 [thread overview]
Message-ID: <abolyGbAyynUANDB@hyeyoo> (raw)
In-Reply-To: <aa_R-6SdHYBBkQX-@hyeyoo>
#syz test
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index d79acf5c5100..b7be2cc1efc3 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -50,8 +50,8 @@
*
* The kmemleak_object structures have a use_count incremented or decremented
* using the get_object()/put_object() functions. When the use_count becomes
- * 0, this count can no longer be incremented and put_object() schedules the
- * kmemleak_object freeing via an RCU callback. All calls to the get_object()
+ * 0, this count can no longer be incremented and put_object() adds the
+ * kmemleak_object to a deferred free list. All calls to the get_object()
* function must be protected by rcu_read_lock() to avoid accessing a freed
* structure.
*/
@@ -93,6 +93,7 @@
#include <linux/mm.h>
#include <linux/workqueue.h>
#include <linux/crc32.h>
+#include <linux/llist.h>
#include <asm/sections.h>
#include <asm/processor.h>
@@ -138,7 +139,7 @@ struct kmemleak_object {
struct list_head object_list;
struct list_head gray_list;
struct rb_node rb_node;
- struct rcu_head rcu; /* object_list lockless traversal */
+ struct llist_node free_node; /* deferred freeing */
/* object usage count; object freed when use_count == 0 */
atomic_t use_count;
unsigned int del_state; /* deletion state */
@@ -209,6 +210,13 @@ static DEFINE_RAW_SPINLOCK(kmemleak_lock);
static struct kmem_cache *object_cache;
static struct kmem_cache *scan_area_cache;
+/* objects pending RCU-deferred freeing */
+static LLIST_HEAD(objects_to_free);
+static atomic_long_t objects_to_free_count;
+static void flush_deferred_frees_work(struct work_struct *work);
+static DECLARE_WORK(deferred_free_work, flush_deferred_frees_work);
+#define DEFERRED_FREE_BATCH 256
+
/* set if tracing memory operations is enabled */
static int kmemleak_enabled __read_mostly = 1;
/* same as above but only for the kmemleak_free() callback */
@@ -522,14 +530,12 @@ static void mem_pool_free(struct kmemleak_object *object)
}
/*
- * RCU callback to free a kmemleak_object.
+ * Free a kmemleak_object and its associated scan areas.
*/
-static void free_object_rcu(struct rcu_head *rcu)
+static void free_object(struct kmemleak_object *object)
{
struct hlist_node *tmp;
struct kmemleak_scan_area *area;
- struct kmemleak_object *object =
- container_of(rcu, struct kmemleak_object, rcu);
/*
* Once use_count is 0 (guaranteed by put_object), there is no other
@@ -543,11 +549,19 @@ static void free_object_rcu(struct rcu_head *rcu)
}
/*
- * Decrement the object use_count. Once the count is 0, free the object using
- * an RCU callback. Since put_object() may be called via the kmemleak_free() ->
- * delete_object() path, the delayed RCU freeing ensures that there is no
- * recursive call to the kernel allocator. Lock-less RCU object_list traversal
- * is also possible.
+ * Decrement the object use_count. Once the count is 0, add the object to the
+ * deferred free list. Since put_object() may be called via the
+ * kmemleak_free() -> delete_object() path, the deferred freeing ensures that
+ * there is no recursive call to the kernel allocator. Lock-less RCU
+ * object_list traversal is also possible. The actual freeing happens after
+ * an RCU grace period in flush_deferred_frees().
+ *
+ * Unlike the previous call_rcu()-based approach, this avoids embedding
+ * rcu_head in kmemleak_object. Objects from SLAB_NOLEAKTRACE caches (like
+ * kmemleak's own object_cache) are not tracked by kmemleak. When such
+ * objects were linked in the call_rcu callback chain via rcu_head->next,
+ * kmemleak could not scan through them, breaking the chain and causing
+ * false positive leak reports for objects queued after them.
*/
static void put_object(struct kmemleak_object *object)
{
@@ -558,14 +572,46 @@ static void put_object(struct kmemleak_object *object)
WARN_ON(object->flags & OBJECT_ALLOCATED);
/*
- * It may be too early for the RCU callbacks, however, there is no
+ * It may be too early for deferred freeing, however, there is no
* concurrent object_list traversal when !object_cache and all objects
* came from the memory pool. Free the object directly.
*/
- if (object_cache)
- call_rcu(&object->rcu, free_object_rcu);
- else
- free_object_rcu(&object->rcu);
+ if (object_cache) {
+ llist_add(&object->free_node, &objects_to_free);
+ if (atomic_long_inc_return(&objects_to_free_count) >=
+ DEFERRED_FREE_BATCH)
+ schedule_work(&deferred_free_work);
+ } else {
+ free_object(object);
+ }
+}
+
+/*
+ * Flush all deferred object frees after an RCU grace period. This must be
+ * called from a context that can block.
+ */
+static void flush_deferred_frees(void)
+{
+ struct llist_node *list;
+ struct kmemleak_object *object, *tmp;
+ long count = 0;
+
+ list = llist_del_all(&objects_to_free);
+ if (!list)
+ return;
+
+ synchronize_rcu();
+
+ llist_for_each_entry_safe(object, tmp, list, free_node) {
+ free_object(object);
+ count++;
+ }
+ atomic_long_sub(count, &objects_to_free_count);
+}
+
+static void flush_deferred_frees_work(struct work_struct *work)
+{
+ flush_deferred_frees();
}
/*
@@ -809,7 +855,7 @@ static void create_object_percpu(unsigned long ptr, size_t size,
}
/*
- * Mark the object as not allocated and schedule RCU freeing via put_object().
+ * Mark the object as not allocated and schedule deferred freeing via put_object().
*/
static void __delete_object(struct kmemleak_object *object)
{
@@ -2209,6 +2255,7 @@ static void __kmemleak_do_cleanup(void)
if (!(++cnt & 0x3f))
cond_resched();
}
+ flush_deferred_frees();
}
/*
diff --git a/mm/slub.c b/mm/slub.c
index 20cb4f3b636d..6bdf409d427e 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -7537,6 +7537,7 @@ static void early_kmem_cache_node_alloc(int node)
n = kasan_slab_alloc(kmem_cache_node, n, GFP_KERNEL, false);
slab->freelist = get_freepointer(kmem_cache_node, n);
slab->inuse = 1;
+ kmemleak_alloc(n, sizeof(*n), 1, GFP_NOWAIT);
kmem_cache_node->node[node] = n;
init_kmem_cache_node(n, NULL);
inc_slabs_node(kmem_cache_node, node, slab->objects);
--
2.43.0
next prev parent reply other threads:[~2026-03-18 4:12 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-09 18:26 [syzbot] [mm?] [f2fs?] [exfat?] memory leak in __kfree_rcu_sheaf syzbot
2026-03-02 3:41 ` Qing Wang
2026-03-02 3:57 ` syzbot
2026-03-02 8:39 ` Vlastimil Babka (SUSE)
2026-03-04 1:30 ` Harry Yoo
2026-03-04 13:39 ` Vlastimil Babka (SUSE)
2026-03-06 19:35 ` Catalin Marinas
2026-03-08 11:02 ` Catalin Marinas
2026-03-08 12:31 ` syzbot
2026-03-08 11:04 ` Catalin Marinas
2026-03-08 12:42 ` syzbot
2026-03-09 10:46 ` Harry Yoo
2026-03-09 11:11 ` syzbot
2026-03-09 12:17 ` Harry Yoo
2026-03-09 20:31 ` Catalin Marinas
2026-03-11 3:04 ` Harry Yoo
2026-03-11 3:20 ` Harry Yoo
2026-03-10 3:39 ` Harry Yoo
2026-03-10 3:54 ` syzbot
2026-03-10 6:11 ` Harry Yoo
2026-03-10 6:29 ` syzbot
2026-03-10 8:10 ` Harry Yoo
2026-03-10 9:40 ` syzbot
2026-03-18 2:34 ` Harry Yoo
2026-03-18 3:08 ` syzbot
2026-03-18 4:10 ` Harry Yoo [this message]
2026-03-18 5:02 ` syzbot
2026-03-11 9:57 ` Qing Wang
2026-03-11 10:17 ` syzbot
2026-03-11 10:48 ` Qing Wang
2026-03-11 11:03 ` syzbot
2026-03-11 11:23 ` Harry Yoo
2026-03-20 0:06 ` Harry Yoo
2026-03-20 10:34 ` syzbot
2026-03-20 11:20 ` Harry Yoo
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=abolyGbAyynUANDB@hyeyoo \
--to=harry.yoo@oracle.com \
--cc=akpm@linux-foundation.org \
--cc=catalin.marinas@arm.com \
--cc=chao@kernel.org \
--cc=hao.li@linux.dev \
--cc=jaegeuk@kernel.org \
--cc=jannh@google.com \
--cc=liam.howlett@oracle.com \
--cc=linkinjeon@kernel.org \
--cc=linux-f2fs-devel@lists.sourceforge.net \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=lorenzo.stoakes@oracle.com \
--cc=pfalcato@suse.de \
--cc=sj1557.seo@samsung.com \
--cc=syzbot+cae7809e9dc1459e4e63@syzkaller.appspotmail.com \
--cc=syzkaller-bugs@googlegroups.com \
--cc=vbabka@kernel.org \
--cc=vbabka@suse.cz \
--cc=wangqing7171@gmail.com \
/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