Skip to content

Commit a243f7f

Browse files
Tzung-Bi Shihgregkh
authored andcommitted
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>
1 parent 4d7dc4d commit a243f7f

1 file changed

Lines changed: 41 additions & 0 deletions

File tree

drivers/base/revocable_test.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
*
1515
* - Try Access Macro: Same as "Revocation" but uses the
1616
* 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.
1720
*/
1821

1922
#include <kunit/test.h>
23+
#include <linux/refcount.h>
2024
#include <linux/revocable.h>
2125

2226
static void revocable_test_basic(struct kunit *test)
@@ -127,11 +131,48 @@ static void revocable_test_try_access_macro2(struct kunit *test)
127131
revocable_free(rev);
128132
}
129133

134+
static void revocable_test_provider_use_after_free(struct kunit *test)
135+
{
136+
struct revocable_provider __rcu *rp;
137+
struct revocable_provider *old_rp;
138+
void *real_res = (void *)0x12345678;
139+
struct revocable *rev;
140+
141+
rp = revocable_provider_alloc(real_res);
142+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp);
143+
144+
rev = revocable_alloc(NULL);
145+
KUNIT_EXPECT_PTR_EQ(test, rev, NULL);
146+
147+
/* Simulate the provider has been freed. */
148+
old_rp = rcu_replace_pointer(rp, NULL, 1);
149+
rev = revocable_alloc(rp);
150+
KUNIT_EXPECT_PTR_EQ(test, rev, NULL);
151+
rcu_replace_pointer(rp, old_rp, 1);
152+
153+
struct {
154+
struct srcu_struct srcu;
155+
void __rcu *res;
156+
struct kref kref;
157+
struct rcu_head rcu;
158+
} *rp_internal = (void *)rp;
159+
160+
/* Simulate the provider is releasing. */
161+
refcount_set(&rp_internal->kref.refcount, 0);
162+
rev = revocable_alloc(rp);
163+
KUNIT_EXPECT_PTR_EQ(test, rev, NULL);
164+
refcount_set(&rp_internal->kref.refcount, 1);
165+
166+
revocable_provider_revoke(&rp);
167+
KUNIT_EXPECT_PTR_EQ(test, unrcu_pointer(rp), NULL);
168+
}
169+
130170
static struct kunit_case revocable_test_cases[] = {
131171
KUNIT_CASE(revocable_test_basic),
132172
KUNIT_CASE(revocable_test_revocation),
133173
KUNIT_CASE(revocable_test_try_access_macro),
134174
KUNIT_CASE(revocable_test_try_access_macro2),
175+
KUNIT_CASE(revocable_test_provider_use_after_free),
135176
{}
136177
};
137178

0 commit comments

Comments
 (0)