From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793249209; b=5liQIXJ5Re7MNm6Bgt5LXTzFa6/3EP85wfSA+AnoFtLgoXXuuIjlizkeGioVwFv5lXpq 9jWbR+ZxSRcZXqDFqANpxkqArP60Dim8C/F97AObJrZny1Pz9N6VUl6+7N2tYghg1bqmz NwRZLRy2rLBnZvtCCCjlf6NQcsGVPXzIsGh5bBvzNKepVWj/lCPeGzdJc3TXXESzrlfDr 0CBDVioxfBqsYhCmBiEABj1o1H+Bg2xz/4JoX6+vJfpfRzpabq//ILlSZW5T7VH+OwmcQ +FqOiXdEi/X+tvz74fOJR//6jIrZ4DKqHDwAXTUYWjBlu2cvO6WrvykXoKCTCHESOFA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793249209; h=Received:DKIM-Signature:Date:To:From:Subject:Message-Id; bh=BVFbCcn5joYRr5OEgTSflDka9tIyBoC2L2oeOVKIDRg=; b=qhXc1TuwxayTOzl9qmRTxoEFt2aeEOj/Tb3j1SKuTwUJD7ELjyBdZDtcHWZEFj2QRxC9 509oCmVABrbg+4oymLE4MtQBtoWwcBrUlIO/6p/wObPuuGiuFgsP5JqoqgqUX69+H46Hj vStJViOc6vldZnnw/MyMcqHOs5sbkKsvjEWsN2GiuwngP5+5PjYaLPuWoBybRtKpZthA/ pZBRNE1yZ2ANtB7+qE3AGxs0rV/VscS07qilHwdidR7VvCXqRzr9zvLFUR0ce3G7QXEbB tVAHCUl3yXTiPxCM03rVroOuep/t6WFC3ZApxofZfvVWJ8FywRXsOZ7aciGnc9rdHww== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linux-foundation.org header.i=@linux-foundation.org header.a=rsa-sha256 header.s=korg header.b=s+YrHRez; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C2D8D606A31D for ; Thu, 29 Oct 2026 04:46:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux-foundation.org header.i=@linux-foundation.org header.a=rsa-sha256 header.s=korg header.b=s+YrHRez Received: by smtp.kernel.org (Postfix) with ESMTPSA id 216171F01825; Thu, 29 Oct 2026 04:46:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux-foundation.org; s=korg; t=1793249208; bh=BVFbCcn5joYRr5OEgTSflDka9tIyBoC2L2oeOVKIDRg=; h=Date:To:From:Subject; b=s+YrHRez1CeffZ6smgBQem6qVQIF0c+XhSQfnWgN9+FEvR1nkgt65zBhJnsgOAUH5 fc34rZLfBUwFiJvkaPEZ8g8fqOdXTwR5lst/Uqgl1aiPNE55+nUpNqAqZ+nRuDsFaI IREbG/iu8TFMbFAXZn4ZKo73yFqXP8Yir/UNwG/k= Date: Wed, 28 Oct 2026 21:46:47 -0700 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 From: Andrew Morton Subject: + mm-kasan-kunit-add-double-free-tests-involving-kfree_rcu_sheaf.patch added to mm-hotfixes-unstable branch Message-Id: <20261029044648.216171F01825@smtp.kernel.org> X-Mailing-List: mm-commits@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list 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 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 ... # 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: ... 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 Signed-off-by: Andrew Morton Reviewed-by: Alexander Potapenko Cc: Andrey Ryabinin Cc: Vlastimil Babka Cc: Harry Yoo Cc: Andrey Konovalov Cc: Dmitry Vyukov Cc: Vincenzo Frascino Cc: Hao Li Cc: Christoph Lameter Cc: David Rientjes Cc: Roman Gushchin Cc: --- 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 #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