*
* - Try Access Macro: Same as "Revocation" but uses the
* REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED().
+ *
+ * - Provider Use-after-free: Verifies revocable_alloc() correctly handles
+ * race conditions where the provider is being released.
*/
#include <kunit/test.h>
+#include <linux/refcount.h>
#include <linux/revocable.h>
static void revocable_test_basic(struct kunit *test)
revocable_free(rev);
}
+static void revocable_test_provider_use_after_free(struct kunit *test)
+{
+ struct revocable_provider __rcu *rp;
+ struct revocable_provider *old_rp;
+ void *real_res = (void *)0x12345678;
+ struct revocable *rev;
+
+ rp = revocable_provider_alloc(real_res);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp);
+
+ rev = revocable_alloc(NULL);
+ KUNIT_EXPECT_PTR_EQ(test, rev, NULL);
+
+ /* Simulate the provider has been freed. */
+ old_rp = rcu_replace_pointer(rp, NULL, 1);
+ rev = revocable_alloc(rp);
+ KUNIT_EXPECT_PTR_EQ(test, rev, NULL);
+ rcu_replace_pointer(rp, old_rp, 1);
+
+ struct {
+ struct srcu_struct srcu;
+ void __rcu *res;
+ struct kref kref;
+ struct rcu_head rcu;
+ } *rp_internal = (void *)rp;
+
+ /* Simulate the provider is releasing. */
+ refcount_set(&rp_internal->kref.refcount, 0);
+ rev = revocable_alloc(rp);
+ KUNIT_EXPECT_PTR_EQ(test, rev, NULL);
+ refcount_set(&rp_internal->kref.refcount, 1);
+
+ revocable_provider_revoke(&rp);
+ KUNIT_EXPECT_PTR_EQ(test, unrcu_pointer(rp), NULL);
+}
+
static struct kunit_case revocable_test_cases[] = {
KUNIT_CASE(revocable_test_basic),
KUNIT_CASE(revocable_test_revocation),
KUNIT_CASE(revocable_test_try_access_macro),
KUNIT_CASE(revocable_test_try_access_macro2),
+ KUNIT_CASE(revocable_test_provider_use_after_free),
{}
};