From: Andrew Morton <akpm@linux-foundation.org>
To: mm-commits@vger.kernel.org,vincenzo.frascino@arm.com,vbabka@kernel.org,
stable@kernel.orgg,ryabinin.a.a@gmail.com,roman.gushchin@linux.dev,
rientjes@google.com,harry@kernel.org,hao.li@linux.dev,glider@google.com,
dvyukov@google.com,cl@gentwo.org,andreyknvl@gmail.com,kuniyu@google.com,
akpm@linux-foundation.org
Subject: + mm-kasan-kunit-add-double-free-tests-involving-kfree_rcu_sheaf.patch added to mm-hotfixes-unstable branch
Date: Wed, 28 Oct 2026 21:46:47 -0700 [thread overview]
Message-ID: <20261029044648.216171F01825@smtp.kernel.org> (raw)
The patch titled
Subject: mm/kasan/kunit: add double-free tests involving kfree_rcu_sheaf().
has been added to the -mm mm-hotfixes-unstable branch. Its filename is
mm-kasan-kunit-add-double-free-tests-involving-kfree_rcu_sheaf.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/mm-kasan-kunit-add-double-free-tests-involving-kfree_rcu_sheaf.patch
This patch will later appear in the mm-hotfixes-unstable branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: Kuniyuki Iwashima <kuniyu@google.com>
Subject: mm/kasan/kunit: add double-free tests involving kfree_rcu_sheaf().
Date: Wed, 28 Oct 2026 19:39:20 +0000
The new test cases verify that KASAN immediately detects all
three combinations of double-free involving kfree_rcu_sheaf():
1. kmem_cache_free() -> kfree_rcu()
2. kfree_rcu() -> kmem_cache_free()
3. kfree_rcu() -> kfree_rcu()
Each test also evicts the object from the KASAN quarantine and
reallocates it as a victim object before flushing the RCU sheaf
to ensure the live victim object is not poisoned by the rejected
double-free.
Without the patch, all tests fail to detect the 2nd free, and
the victim is poisoned:
# kmem_cache_double_free_rcu_1: EXPECTATION FAILED at mm/kasan/kasan_test_c.c:1096
KASAN failure expected in "kfree_rcu(p, rcu)", but none occurred
... <splat below is logged here>
# kmem_cache_double_free_rcu_1: EXPECTATION FAILED at mm/kasan/kasan_test_c.c:1156
Expected (<...snipped READ_ONCE()...>&(victim->i)); }) == 42, but
<...snipped READ_ONCE()...>victim->i == 117729280 (0x7046800)
# kasan: pass:0 fail:3 skip:0 total:3
# Totals: pass:0 fail:3 skip:0 total:3
not ok 1 kasan
In all cases, KASAN logs a false-positive use-after-free splat at
READ_ONCE(victim->i), and it has no information about the suspect;
the two stack traces ("Call Trace:" and "Allocated by") are of the
live victim, and "Freed by" is of the RCU task.
BUG: KASAN: slab-use-after-free in __kmem_cache_double_free_rcu+0x9ee/0xaf0
Read of size 4 at addr ffff8881076c2938 by task kunit_try_catch/513
...
Freed by task 0:
...
__rcu_free_sheaf_prepare+0xf6/0x240
rcu_free_sheaf_nobarn+0x31/0xe0
rcu_core+0x75e/0x16b0
With the patch, all tests can detect the 2nd free immediately and
prevent the victim from being poisoned:
# kasan: pass:3 fail:0 skip:0 total:3
# Totals: pass:3 fail:0 skip:0 total:3
ok 1 kasan
All the stack traces point to the same object for double-free,
and the two free traces are shown in "Call Trace:" and "Freed by"
(or "Last potentially related work creation:" in the 2nd/3rd test
cases).
BUG: KASAN: double-free in kvfree_call_rcu+0xb6/0x3c0
Free of addr ffff888108495938 by task kunit_try_catch/511
...
Call Trace:
<TASK>
...
check_slab_allocation+0x10d/0x120
__kfree_rcu_sheaf+0x1c/0x2d0
kvfree_call_rcu+0xb6/0x3c0
__kmem_cache_double_free_rcu+0x310/0xaf0
Allocated by task 511:
...
kmem_cache_alloc_noprof+0x14f/0x3c0
__kmem_cache_double_free_rcu+0x152/0xaf0
Freed by task 511:
...
kmem_cache_free+0x14f/0x3c0
__kmem_cache_double_free_rcu+0x2c6/0xaf0
Notably, in the 2nd/3rd test cases, we can detect the 2nd free
before the object is poisoned:
Memory state around the buggy address:
ffff888106dc7800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888106dc7880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888106dc7900: fc fc fc fc fc fc fc 00 00 00 fc fc fc fc fc fc
^
...
ok 2 kmem_cache_double_free_rcu_2
Link: https://lore.kernel.org/20261028194014.3671161-3-kuniyu@google.com
Fixes: ec66e0d59952 ("slab: add sheaf support for batching kfree_rcu() operations")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Alexander Potapenko <glider@google.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Harry Yoo <harry@kernel.org>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Cc: Hao Li <hao.li@linux.dev>
Cc: Christoph Lameter <cl@gentwo.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: <stable@kernel.orgg>
---
mm/kasan/kasan_test_c.c | 139 ++++++++++++++++++++++++++++++++++++++
1 file changed, 139 insertions(+)
--- a/mm/kasan/kasan_test_c.c~mm-kasan-kunit-add-double-free-tests-involving-kfree_rcu_sheaf
+++ a/mm/kasan/kasan_test_c.c
@@ -30,6 +30,7 @@
#include <asm/page.h>
#include "kasan.h"
+#include "../slab.h"
#define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_GRANULE_SIZE)
@@ -1070,6 +1071,139 @@ static void kmem_cache_double_free(struc
kmem_cache_destroy(cache);
}
+#ifndef MODULE
+static void __kmem_cache_double_free_rcu(struct kunit *test, int mode)
+{
+ struct kasan_rcu_info *p, *victim = NULL, **objs;
+ struct kmem_cache *cache;
+ int i, n = 0;
+
+ KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_GENERIC);
+ KASAN_TEST_NEEDS_CONFIG_OFF(test, CONFIG_PREEMPT_RT);
+
+ cache = kmem_cache_create("test_cache", sizeof(*p), 0, SLAB_NO_MERGE,
+ NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cache);
+
+ if (!cache->sheaf_capacity) {
+ kmem_cache_destroy(cache);
+ kunit_skip(test, "Test requires sheaves");
+ }
+
+ objs = kunit_kcalloc(test, cache->sheaf_capacity * 2, sizeof(*objs),
+ GFP_KERNEL);
+ if (!objs) {
+ kunit_err(test, "Allocation failed: %s\n", __func__);
+ goto out;
+ }
+
+ migrate_disable();
+
+ p = kmem_cache_alloc(cache, GFP_KERNEL);
+ if (!p) {
+ migrate_enable();
+ kunit_err(test, "Allocation failed: %s\n", __func__);
+ goto out;
+ }
+
+ switch (mode) {
+ case 1:
+ kmem_cache_free(cache, p);
+ /* p is already in quarantine. */
+ KUNIT_EXPECT_KASAN_FAIL(test, kfree_rcu(p, rcu));
+ migrate_enable();
+ break;
+ case 2:
+ /* The actual free order is inverse. */
+ kfree_rcu(p, rcu);
+ KUNIT_EXPECT_KASAN_FAIL(test, kmem_cache_free(cache, p));
+ migrate_enable();
+ break;
+ case 3:
+ kfree_rcu(p, rcu);
+
+ for (i = 0; i < cache->sheaf_capacity - 1; i++) {
+ objs[i] = kmem_cache_alloc(cache, GFP_KERNEL);
+ kfree_rcu(objs[i], rcu);
+ }
+
+ KUNIT_EXPECT_KASAN_FAIL(test, kfree_rcu(p, rcu));
+ migrate_enable();
+
+ /*
+ * Move the 1st full sheaf to quarantine, keeping the
+ * 2nd in pcs->rcu_free.
+ */
+ rcu_barrier();
+ break;
+ }
+
+ /*
+ * Move p to quarantine if not poisoned yet (case 2,
+ * or fallback in case 3).
+ */
+ if (kasan_byte_accessible(p))
+ kvfree_rcu_barrier_on_cache(cache);
+
+ /*
+ * Evict p from quarantine and reallocate it as a live victim object
+ * before the RCU sheaf is flushed.
+ */
+ kasan_quarantine_remove_cache(cache);
+
+ for (i = 0; i < cache->sheaf_capacity * 2; i++) {
+ objs[n] = kmem_cache_alloc(cache, GFP_KERNEL);
+ if (!objs[n])
+ break;
+ if (objs[n] == p) {
+ victim = objs[n];
+ break;
+ }
+ n++;
+ }
+
+ for (i = 0; i < n; i++)
+ kmem_cache_free(cache, objs[i]);
+
+ KUNIT_EXPECT_PTR_EQ(test, victim, p);
+ if (victim != p)
+ goto out;
+
+ WRITE_ONCE(victim->i, 42);
+
+ /*
+ * If kfree_rcu() failed to reject the double-free above,
+ * flushing the RCU sheaf will silently poison the live
+ * victim and cause a false-positive UAF on victim->i.
+ */
+ kvfree_rcu_barrier_on_cache(cache);
+ KUNIT_EXPECT_EQ(test, READ_ONCE(victim->i), 42);
+
+ if (!READ_ONCE(test_status.report_found))
+ kmem_cache_free(cache, victim);
+out:
+ kmem_cache_destroy(cache);
+}
+
+static void kmem_cache_double_free_rcu_1(struct kunit *test)
+{
+ /* kmem_cache_free() -> kfree_rcu() */
+ __kmem_cache_double_free_rcu(test, 1);
+}
+
+static void kmem_cache_double_free_rcu_2(struct kunit *test)
+{
+ /* kfree_rcu() -> kmem_cache_free() */
+ __kmem_cache_double_free_rcu(test, 2);
+}
+
+static void kmem_cache_double_free_rcu_3(struct kunit *test)
+{
+ /* kfree_rcu() -> kfree_rcu() */
+ __kmem_cache_double_free_rcu(test, 3);
+}
+#endif
+
static void kmem_cache_invalid_free(struct kunit *test)
{
char *p;
@@ -2239,6 +2373,11 @@ static struct kunit_case kasan_kunit_tes
KUNIT_CASE(kfree_via_phys),
KUNIT_CASE(kmem_cache_oob),
KUNIT_CASE(kmem_cache_double_free),
+#ifndef MODULE
+ KUNIT_CASE(kmem_cache_double_free_rcu_1),
+ KUNIT_CASE(kmem_cache_double_free_rcu_2),
+ KUNIT_CASE(kmem_cache_double_free_rcu_3),
+#endif
KUNIT_CASE(kmem_cache_invalid_free),
KUNIT_CASE(kmem_cache_rcu_uaf),
KUNIT_CASE(kmem_cache_rcu_reuse),
_
Patches currently in -mm which might be from kuniyu@google.com are
mm-kasan-detect-double-free-involving-kfree_rcu_sheaf.patch
mm-kasan-kunit-add-double-free-tests-involving-kfree_rcu_sheaf.patch
reply other threads:[~2026-10-29 4:46 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=20261029044648.216171F01825@smtp.kernel.org \
--to=akpm@linux-foundation.org \
--cc=andreyknvl@gmail.com \
--cc=cl@gentwo.org \
--cc=dvyukov@google.com \
--cc=glider@google.com \
--cc=hao.li@linux.dev \
--cc=harry@kernel.org \
--cc=kuniyu@google.com \
--cc=mm-commits@vger.kernel.org \
--cc=rientjes@google.com \
--cc=roman.gushchin@linux.dev \
--cc=ryabinin.a.a@gmail.com \
--cc=stable@kernel.orgg \
--cc=vbabka@kernel.org \
--cc=vincenzo.frascino@arm.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.