From: Harry Yoo via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net>
To: syzbot <syzbot+cae7809e9dc1459e4e63@syzkaller.appspotmail.com>
Cc: wangqing7171@gmail.com, vbabka@kernel.org,
lorenzo.stoakes@oracle.com, jannh@google.com,
catalin.marinas@arm.com, syzkaller-bugs@googlegroups.com,
linux-kernel@vger.kernel.org, liam.howlett@oracle.com,
linux-f2fs-devel@lists.sourceforge.net, linux-mm@kvack.org,
hao.li@linux.dev, pfalcato@suse.de,
linux-fsdevel@vger.kernel.org, jaegeuk@kernel.org,
akpm@linux-foundation.org, sj1557.seo@samsung.com,
linkinjeon@kernel.org, vbabka@suse.cz
Subject: Re: [f2fs-dev] [syzbot] [mm?] [f2fs?] [exfat?] memory leak in __kfree_rcu_sheaf
Date: Wed, 18 Mar 2026 11:34:16 +0900 [thread overview]
Message-ID: <aboPKERGvOniN-OK@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();
}
/*
base-commit: fda995dadf2960405545e5002aaa85207aa758cf
--
2.43.0
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
WARNING: multiple messages have this Message-ID (diff)
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 11:34:16 +0900 [thread overview]
Message-ID: <aboPKERGvOniN-OK@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();
}
/*
base-commit: fda995dadf2960405545e5002aaa85207aa758cf
--
2.43.0
next prev parent reply other threads:[~2026-03-18 2:44 UTC|newest]
Thread overview: 85+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-09 18:26 [f2fs-dev] [syzbot] [mm?] [f2fs?] [exfat?] memory leak in __kfree_rcu_sheaf syzbot
2026-02-09 18:26 ` syzbot
2026-03-02 3:41 ` [f2fs-dev] " Qing Wang
2026-03-02 3:41 ` Qing Wang
2026-03-02 3:57 ` [f2fs-dev] " syzbot
2026-03-02 3:57 ` syzbot
2026-03-02 8:39 ` [f2fs-dev] " Vlastimil Babka (SUSE) via Linux-f2fs-devel
2026-03-02 8:39 ` Vlastimil Babka (SUSE)
2026-03-04 1:30 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-04 1:30 ` Harry Yoo
2026-03-04 13:39 ` [f2fs-dev] " Vlastimil Babka (SUSE) via Linux-f2fs-devel
2026-03-04 13:39 ` Vlastimil Babka (SUSE)
2026-03-06 19:35 ` [f2fs-dev] " Catalin Marinas
2026-03-06 19:35 ` Catalin Marinas
2026-03-08 11:02 ` [f2fs-dev] " Catalin Marinas
2026-03-08 11:02 ` Catalin Marinas
2026-03-08 12:31 ` [f2fs-dev] " syzbot
2026-03-08 12:31 ` syzbot
2026-03-08 11:04 ` [f2fs-dev] " Catalin Marinas
2026-03-08 11:04 ` Catalin Marinas
2026-03-08 12:42 ` [f2fs-dev] " syzbot
2026-03-08 12:42 ` syzbot
2026-03-09 10:46 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-09 10:46 ` Harry Yoo
2026-03-09 11:11 ` [f2fs-dev] " syzbot
2026-03-09 11:11 ` syzbot
2026-03-09 12:17 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-09 12:17 ` Harry Yoo
2026-03-09 20:31 ` [f2fs-dev] " Catalin Marinas
2026-03-09 20:31 ` Catalin Marinas
2026-03-11 3:04 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-11 3:04 ` Harry Yoo
2026-03-11 3:20 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-11 3:20 ` Harry Yoo
2026-03-10 3:39 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-10 3:39 ` Harry Yoo
2026-03-10 3:54 ` [f2fs-dev] " syzbot
2026-03-10 3:54 ` syzbot
2026-03-10 6:11 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-10 6:11 ` Harry Yoo
2026-03-10 6:29 ` [f2fs-dev] " syzbot
2026-03-10 6:29 ` syzbot
2026-03-10 8:10 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-10 8:10 ` Harry Yoo
2026-03-10 9:40 ` [f2fs-dev] " syzbot
2026-03-10 9:40 ` syzbot
2026-03-18 2:34 ` Harry Yoo via Linux-f2fs-devel [this message]
2026-03-18 2:34 ` Harry Yoo
2026-03-18 3:08 ` [f2fs-dev] " syzbot
2026-03-18 3:08 ` syzbot
2026-03-18 4:10 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-18 4:10 ` Harry Yoo
2026-03-18 5:02 ` [f2fs-dev] " syzbot
2026-03-18 5:02 ` syzbot
2026-03-11 9:57 ` [f2fs-dev] " Qing Wang
2026-03-11 9:57 ` Qing Wang
2026-03-11 10:17 ` [f2fs-dev] " syzbot
2026-03-11 10:17 ` syzbot
2026-03-11 10:48 ` [f2fs-dev] " Qing Wang
2026-03-11 10:48 ` Qing Wang
2026-03-11 11:03 ` [f2fs-dev] " syzbot
2026-03-11 11:03 ` syzbot
2026-03-11 11:23 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-11 11:23 ` Harry Yoo
2026-03-20 0:06 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-20 0:06 ` Harry Yoo
2026-03-20 10:34 ` [f2fs-dev] " syzbot
2026-03-20 10:34 ` syzbot
2026-03-20 11:20 ` [f2fs-dev] " Harry Yoo via Linux-f2fs-devel
2026-03-20 11:20 ` Harry Yoo
2026-05-02 10:09 ` David Timber
2026-05-03 6:00 ` David Timber
2026-05-03 7:17 ` [f2fs-dev] [syzbot] [mm?] [exfat?] [f2fs?] " syzbot
2026-05-03 7:17 ` syzbot
2026-05-03 6:05 ` [syzbot] [mm?] [f2fs?] [exfat?] " David Timber
2026-05-03 7:27 ` [f2fs-dev] [syzbot] [mm?] [exfat?] [f2fs?] " syzbot
2026-05-03 7:27 ` syzbot
2026-05-03 7:41 ` [f2fs-dev] " David Timber via Linux-f2fs-devel
2026-05-03 7:41 ` David Timber
2026-05-04 20:17 ` [syzbot] [mm?] [f2fs?] [exfat?] " David Timber
2026-05-04 20:51 ` [f2fs-dev] [syzbot] [mm?] [exfat?] [f2fs?] " syzbot
2026-05-04 20:51 ` syzbot
2026-05-04 20:26 ` [syzbot] [mm?] [f2fs?] [exfat?] " David Timber
2026-05-04 21:12 ` [f2fs-dev] [syzbot] [mm?] [exfat?] [f2fs?] " syzbot
2026-05-04 21:12 ` syzbot
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=aboPKERGvOniN-OK@hyeyoo \
--to=linux-f2fs-devel@lists.sourceforge.net \
--cc=akpm@linux-foundation.org \
--cc=catalin.marinas@arm.com \
--cc=hao.li@linux.dev \
--cc=harry.yoo@oracle.com \
--cc=jaegeuk@kernel.org \
--cc=jannh@google.com \
--cc=liam.howlett@oracle.com \
--cc=linkinjeon@kernel.org \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.