Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

revocable: Add KUnit test for provider lifetime races

Add a test to verify that revocable_alloc() correctly handles race
conditions where the provider is being released.

The test covers three scenarios:
1. Allocating from a NULL provider.
2. Allocating from a provider that has been detached (pointer is NULL).
3. Allocating from a provider that is in the process of destruction
(refcount is 0), simulating a race between revocable_alloc() and
revocable_provider_release().

A way to run the test:
$ ./tools/testing/kunit/kunit.py run \
--kconfig_add CONFIG_REVOCABLE_KUNIT_TEST=y \
--kconfig_add CONFIG_PROVE_LOCKING=y \
--kconfig_add CONFIG_DEBUG_KERNEL=y \
--kconfig_add CONFIG_DEBUG_INFO=y \
--kconfig_add CONFIG_DEBUG_INFO_DWARF5=y \
--kconfig_add CONFIG_KASAN=y \
--kconfig_add CONFIG_DETECT_HUNG_TASK=y \
--kconfig_add CONFIG_DEFAULT_HUNG_TASK_TIMEOUT="10" \
--arch=x86_64 --raw_output=all \
revocable_test

Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Link: https://patch.msgid.link/20260129143733.45618-3-tzungbi@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tzung-Bi Shih and committed by
Greg Kroah-Hartman
a243f7fb 4d7dc4d1

+41
+41
drivers/base/revocable_test.c
··· 14 14 * 15 15 * - Try Access Macro: Same as "Revocation" but uses the 16 16 * REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED(). 17 + * 18 + * - Provider Use-after-free: Verifies revocable_alloc() correctly handles 19 + * race conditions where the provider is being released. 17 20 */ 18 21 19 22 #include <kunit/test.h> 23 + #include <linux/refcount.h> 20 24 #include <linux/revocable.h> 21 25 22 26 static void revocable_test_basic(struct kunit *test) ··· 131 127 revocable_free(rev); 132 128 } 133 129 130 + static void revocable_test_provider_use_after_free(struct kunit *test) 131 + { 132 + struct revocable_provider __rcu *rp; 133 + struct revocable_provider *old_rp; 134 + void *real_res = (void *)0x12345678; 135 + struct revocable *rev; 136 + 137 + rp = revocable_provider_alloc(real_res); 138 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp); 139 + 140 + rev = revocable_alloc(NULL); 141 + KUNIT_EXPECT_PTR_EQ(test, rev, NULL); 142 + 143 + /* Simulate the provider has been freed. */ 144 + old_rp = rcu_replace_pointer(rp, NULL, 1); 145 + rev = revocable_alloc(rp); 146 + KUNIT_EXPECT_PTR_EQ(test, rev, NULL); 147 + rcu_replace_pointer(rp, old_rp, 1); 148 + 149 + struct { 150 + struct srcu_struct srcu; 151 + void __rcu *res; 152 + struct kref kref; 153 + struct rcu_head rcu; 154 + } *rp_internal = (void *)rp; 155 + 156 + /* Simulate the provider is releasing. */ 157 + refcount_set(&rp_internal->kref.refcount, 0); 158 + rev = revocable_alloc(rp); 159 + KUNIT_EXPECT_PTR_EQ(test, rev, NULL); 160 + refcount_set(&rp_internal->kref.refcount, 1); 161 + 162 + revocable_provider_revoke(&rp); 163 + KUNIT_EXPECT_PTR_EQ(test, unrcu_pointer(rp), NULL); 164 + } 165 + 134 166 static struct kunit_case revocable_test_cases[] = { 135 167 KUNIT_CASE(revocable_test_basic), 136 168 KUNIT_CASE(revocable_test_revocation), 137 169 KUNIT_CASE(revocable_test_try_access_macro), 138 170 KUNIT_CASE(revocable_test_try_access_macro2), 171 + KUNIT_CASE(revocable_test_provider_use_after_free), 139 172 {} 140 173 }; 141 174