aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/driver-api/driver-model/index.rst1
-rw-r--r--Documentation/driver-api/driver-model/revocable.rst149
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/base/revocable.c225
-rw-r--r--include/linux/revocable.h89
5 files changed, 0 insertions, 471 deletions
diff --git a/Documentation/driver-api/driver-model/index.rst b/Documentation/driver-api/driver-model/index.rst
index 8e1ee21185df..4831bdd92e5c 100644
--- a/Documentation/driver-api/driver-model/index.rst
+++ b/Documentation/driver-api/driver-model/index.rst
@@ -14,7 +14,6 @@ Driver Model
overview
platform
porting
- revocable
.. only:: subproject and html
diff --git a/Documentation/driver-api/driver-model/revocable.rst b/Documentation/driver-api/driver-model/revocable.rst
deleted file mode 100644
index 350e7faeccdc..000000000000
--- a/Documentation/driver-api/driver-model/revocable.rst
+++ /dev/null
@@ -1,149 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-==============================
-Revocable Resource Management
-==============================
-
-Overview
-========
-
-.. kernel-doc:: drivers/base/revocable.c
- :doc: Overview
-
-Revocable vs. Devres (devm)
-===========================
-
-It's important to understand the distinct roles of the Revocable and Devres,
-and how they can complement each other. They address different problems in
-resource management:
-
-* **Devres:** Primarily address **resource leaks**. The lifetime of the
- resources is tied to the lifetime of the device. The resource is
- automatically freed when the device is unbound. This cleanup happens
- irrespective of any potential active users.
-
-* **Revocable:** Primarily addresses **invalid memory access**,
- such as Use-After-Free (UAF). It's an independent synchronization
- primitive that decouples consumer access from the resource's actual
- presence. Consumers interact with a "revocable object" (an intermediary),
- not the underlying resource directly. This revocable object persists as
- long as there are active references to it from consumer handles.
-
-**Key Distinctions & How They Complement Each Other:**
-
-1. **Reference Target:** Consumers of a resource managed by the Revocable
- mechanism hold a reference to the *revocable object*, not the
- encapsulated resource itself.
-
-2. **Resource Lifetime vs. Access:** The underlying resource's lifetime is
- independent of the number of references to the revocable object. The
- resource can be freed at any point. A common scenario is the resource
- being freed by `devres` when the providing device is unbound.
-
-3. **Safe Access:** Revocable provides a safe way to attempt access. Before
- using the resource, a consumer uses the Revocable API (e.g.,
- revocable_try_access()). This function checks if the resource is still
- valid. It returns a pointer to the resource only if it hasn't been
- revoked; otherwise, it returns NULL. This prevents UAF by providing a
- clear signal that the resource is gone.
-
-4. **Complementary Usage:** `devres` and Revocable work well together.
- `devres` can handle the automatic allocation and deallocation of a
- resource tied to a device. The Revocable mechanism can be layered on top
- to provide safe access for consumers whose lifetimes might extend beyond
- the provider device's lifetime. For instance, a userspace program might
- keep a character device file open even after the physical device has been
- removed. In this case:
-
- * `devres` frees the device-specific resource upon unbinding.
- * The Revocable mechanism ensures that any subsequent operations on the
- open file handle, which attempt to access the now-freed resource,
- will fail gracefully (e.g., revocable_try_access() returns NULL)
- instead of causing a UAF.
-
-In summary, `devres` ensures resources are *released* to prevent leaks, while
-the Revocable mechanism ensures that attempts to *access* these resources are
-done safely, even if the resource has been released.
-
-API and Usage
-=============
-
-For Resource Providers
-----------------------
-.. kernel-doc:: drivers/base/revocable.c
- :identifiers: revocable_provider
-
-.. kernel-doc:: drivers/base/revocable.c
- :identifiers: revocable_provider_alloc
-
-.. kernel-doc:: drivers/base/revocable.c
- :identifiers: revocable_provider_revoke
-
-For Resource Consumers
-----------------------
-.. kernel-doc:: include/linux/revocable.h
- :identifiers: revocable
-
-.. kernel-doc:: drivers/base/revocable.c
- :identifiers: revocable_init
-
-.. kernel-doc:: drivers/base/revocable.c
- :identifiers: revocable_deinit
-
-.. kernel-doc:: drivers/base/revocable.c
- :identifiers: revocable_try_access
-
-.. kernel-doc:: drivers/base/revocable.c
- :identifiers: revocable_withdraw_access
-
-.. kernel-doc:: include/linux/revocable.h
- :identifiers: REVOCABLE_TRY_ACCESS_WITH
-
-Example Usage
-~~~~~~~~~~~~~
-
-.. code-block:: c
-
- void consumer_use_resource(struct revocable_provider *rp)
- {
- struct foo_resource *res;
-
- REVOCABLE_TRY_ACCESS_WITH(rp, res);
- // Always check if the resource is valid.
- if (!res) {
- pr_warn("Resource is not available\n");
- return;
- }
-
- // At this point, 'res' is guaranteed to be valid until
- // this block exits.
- do_something_with(res);
-
- } // revocable_withdraw_access() is automatically called here.
-
-.. kernel-doc:: include/linux/revocable.h
- :identifiers: REVOCABLE_TRY_ACCESS_SCOPED
-
-Example Usage
-~~~~~~~~~~~~~
-
-.. code-block:: c
-
- void consumer_use_resource(struct revocable_provider *rp)
- {
- struct foo_resource *res;
-
- REVOCABLE_TRY_ACCESS_SCOPED(rp, res) {
- // Always check if the resource is valid.
- if (!res) {
- pr_warn("Resource is not available\n");
- return;
- }
-
- // At this point, 'res' is guaranteed to be valid until
- // this block exits.
- do_something_with(res);
- }
-
- // revocable_withdraw_access() is automatically called here.
- }
diff --git a/MAINTAINERS b/MAINTAINERS
index 93c07c645c68..1c5ceccc9928 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22385,13 +22385,6 @@ F: include/uapi/linux/rseq.h
F: kernel/rseq.c
F: tools/testing/selftests/rseq/
-REVOCABLE RESOURCE MANAGEMENT
-M: Tzung-Bi Shih <tzungbi@kernel.org>
-L: linux-kernel@vger.kernel.org
-S: Maintained
-F: drivers/base/revocable.c
-F: include/linux/revocable.h
-
RFKILL
M: Johannes Berg <johannes@sipsolutions.net>
L: linux-wireless@vger.kernel.org
diff --git a/drivers/base/revocable.c b/drivers/base/revocable.c
deleted file mode 100644
index 8532ca6a371c..000000000000
--- a/drivers/base/revocable.c
+++ /dev/null
@@ -1,225 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright 2026 Google LLC
- *
- * Revocable resource management
- */
-
-#include <linux/device.h>
-#include <linux/kref.h>
-#include <linux/revocable.h>
-#include <linux/slab.h>
-#include <linux/srcu.h>
-
-/**
- * DOC: Overview
- *
- * The "revocable" mechanism is a synchronization primitive designed to manage
- * safe access to resources that can be asynchronously removed or invalidated.
- * Its primary purpose is to prevent Use-After-Free (UAF) errors when
- * interacting with resources whose lifetimes are not guaranteed to outlast
- * their consumers.
- *
- * This is particularly useful in systems where resources can disappear
- * unexpectedly, such as those provided by hot-pluggable devices like USB.
- * When a consumer holds a reference to such a resource, the underlying device
- * might be removed, causing the resource's memory to be freed. Subsequent
- * access attempts by the consumer would then lead to UAF errors.
- *
- * Revocable addresses this by providing a form of "weak reference" and a
- * controlled access method. It allows a resource consumer to safely attempt to
- * access the resource. The mechanism guarantees that any access granted is
- * valid for the duration of its use. If the resource has already been
- * revoked (i.e., freed), the access attempt will fail safely, typically by
- * returning NULL, instead of causing a crash.
- *
- * The implementation uses a provider/consumer model built on Sleepable
- * RCU (SRCU) to guarantee safe memory access:
- *
- * - A resource provider, such as a driver for a hot-pluggable device,
- * allocates a struct revocable_provider and initializes it with a pointer
- * to the resource.
- *
- * - A resource consumer that wants to access the resource allocates a
- * struct revocable which acts as a handle containing a reference to the
- * provider.
- *
- * - To access the resource, the consumer uses revocable_try_access().
- * This function enters an SRCU read-side critical section and returns
- * the pointer to the resource. If the provider has already freed the
- * resource, it returns NULL. After use, the consumer calls
- * revocable_withdraw_access() to exit the SRCU critical section. The
- * REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED() are
- * convenient helpers for doing that.
- *
- * - When the provider needs to remove the resource, it calls
- * revocable_provider_revoke(). This function sets the internal resource
- * pointer to NULL and then calls synchronize_srcu() to wait for all
- * current readers to finish before the resource can be completely torn
- * down.
- */
-
-/**
- * struct revocable_provider - A handle for resource provider.
- * @srcu: The SRCU to protect the resource.
- * @res: The pointer of resource. It can point to anything.
- * @kref: The refcount for this handle.
- * @rcu: The RCU to protect pointer to itself.
- */
-struct revocable_provider {
- struct srcu_struct srcu;
- void __rcu *res;
- struct kref kref;
- struct rcu_head rcu;
-};
-
-/**
- * revocable_provider_alloc() - Allocate struct revocable_provider.
- * @res: The pointer of resource.
- *
- * This holds an initial refcount to the struct.
- *
- * Return: The pointer of struct revocable_provider. NULL on errors.
- * It enforces the caller handles the returned pointer in RCU ways.
- */
-struct revocable_provider __rcu *revocable_provider_alloc(void *res)
-{
- struct revocable_provider *rp;
-
- rp = kzalloc(sizeof(*rp), GFP_KERNEL);
- if (!rp)
- return NULL;
-
- init_srcu_struct(&rp->srcu);
- RCU_INIT_POINTER(rp->res, res);
- kref_init(&rp->kref);
-
- return (struct revocable_provider __rcu *)rp;
-}
-EXPORT_SYMBOL_GPL(revocable_provider_alloc);
-
-static void revocable_provider_release(struct kref *kref)
-{
- struct revocable_provider *rp = container_of(kref,
- struct revocable_provider, kref);
-
- cleanup_srcu_struct(&rp->srcu);
- kfree_rcu(rp, rcu);
-}
-
-/**
- * revocable_provider_revoke() - Revoke the managed resource.
- * @rp_ptr: The pointer of pointer of resource provider.
- *
- * This sets the resource `(struct revocable_provider *)->res` to NULL to
- * indicate the resource has gone.
- *
- * This drops the refcount to the resource provider. If it is the final
- * reference, revocable_provider_release() will be called to free the struct.
- *
- * It enforces the caller to pass a pointer of pointer of resource provider so
- * that it sets \*rp_ptr to NULL to prevent from keeping a dangling pointer.
- */
-void revocable_provider_revoke(struct revocable_provider __rcu **rp_ptr)
-{
- struct revocable_provider *rp;
-
- rp = rcu_replace_pointer(*rp_ptr, NULL, 1);
- if (!rp)
- return;
-
- rcu_assign_pointer(rp->res, NULL);
- synchronize_srcu(&rp->srcu);
- kref_put(&rp->kref, revocable_provider_release);
-}
-EXPORT_SYMBOL_GPL(revocable_provider_revoke);
-
-/**
- * revocable_init() - Initialize struct revocable.
- * @_rp: The pointer of resource provider.
- * @rev: The pointer of resource consumer.
- *
- * This holds a refcount to the resource provider.
- *
- * Return: 0 on success, -errno otherwise.
- */
-int revocable_init(struct revocable_provider __rcu *_rp, struct revocable *rev)
-{
- struct revocable_provider *rp;
-
- if (!_rp)
- return -ENODEV;
-
- /*
- * Enter a read-side critical section.
- *
- * This prevents kfree_rcu() from freeing the struct revocable_provider
- * memory, for the duration of this scope.
- */
- scoped_guard(rcu) {
- rp = rcu_dereference(_rp);
- if (!rp)
- /* The revocable provider has been revoked. */
- return -ENODEV;
-
- if (!kref_get_unless_zero(&rp->kref))
- /*
- * The revocable provider is releasing (i.e.,
- * revocable_provider_release() has been called).
- */
- return -ENODEV;
- }
- /* At this point, `rp` is safe to access as holding a kref of it */
-
- rev->rp = rp;
- return 0;
-}
-EXPORT_SYMBOL_GPL(revocable_init);
-
-/**
- * revocable_deinit() - Deinitialize struct revocable.
- * @rev: The pointer of struct revocable.
- *
- * This drops a refcount to the resource provider. If it is the final
- * reference, revocable_provider_release() will be called to free the struct.
- */
-void revocable_deinit(struct revocable *rev)
-{
- struct revocable_provider *rp = rev->rp;
-
- kref_put(&rp->kref, revocable_provider_release);
-}
-EXPORT_SYMBOL_GPL(revocable_deinit);
-
-/**
- * revocable_try_access() - Try to access the resource.
- * @rev: The pointer of struct revocable.
- *
- * This tries to de-reference to the resource and enters a RCU critical
- * section.
- *
- * Return: The pointer to the resource. NULL if the resource has gone.
- */
-void *revocable_try_access(struct revocable *rev) __acquires(&rev->rp->srcu)
-{
- struct revocable_provider *rp = rev->rp;
-
- rev->idx = srcu_read_lock(&rp->srcu);
- return srcu_dereference(rp->res, &rp->srcu);
-}
-EXPORT_SYMBOL_GPL(revocable_try_access);
-
-/**
- * revocable_withdraw_access() - Stop accessing to the resource.
- * @rev: The pointer of struct revocable.
- *
- * Call this function to indicate the resource is no longer used. It exits
- * the RCU critical section.
- */
-void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp->srcu)
-{
- struct revocable_provider *rp = rev->rp;
-
- srcu_read_unlock(&rp->srcu, rev->idx);
-}
-EXPORT_SYMBOL_GPL(revocable_withdraw_access);
diff --git a/include/linux/revocable.h b/include/linux/revocable.h
deleted file mode 100644
index e3d6d2c953a3..000000000000
--- a/include/linux/revocable.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright 2026 Google LLC
- */
-
-#ifndef __LINUX_REVOCABLE_H
-#define __LINUX_REVOCABLE_H
-
-#include <linux/compiler.h>
-#include <linux/cleanup.h>
-
-struct device;
-struct revocable_provider;
-
-/**
- * struct revocable - A handle for resource consumer.
- * @rp: The pointer of resource provider.
- * @idx: The index for the RCU critical section.
- */
-struct revocable {
- struct revocable_provider *rp;
- int idx;
-};
-
-struct revocable_provider __rcu *revocable_provider_alloc(void *res);
-void revocable_provider_revoke(struct revocable_provider __rcu **rp);
-
-int revocable_init(struct revocable_provider __rcu *rp, struct revocable *rev);
-void revocable_deinit(struct revocable *rev);
-void *revocable_try_access(struct revocable *rev) __acquires(&rev->rp->srcu);
-void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp->srcu);
-
-DEFINE_FREE(access_rev, struct revocable *, {
- if ((_T)->idx != -1)
- revocable_withdraw_access(_T);
- if ((_T)->rp)
- revocable_deinit(_T);
-})
-
-#define _REVOCABLE_TRY_ACCESS_WITH(_rp, _rev, _res) \
- struct revocable _rev = {.rp = NULL, .idx = -1}; \
- struct revocable *__UNIQUE_ID(name) __free(access_rev) = &_rev; \
- _res = revocable_init(_rp, &_rev) ? NULL : revocable_try_access(&_rev)
-
-/**
- * REVOCABLE_TRY_ACCESS_WITH() - A helper for accessing revocable resource
- * @_rp: The provider's ``struct revocable_provider *`` handle.
- * @_res: A pointer variable that will be assigned the resource.
- *
- * The macro simplifies the access-release cycle for consumers, ensuring that
- * corresponding revocable_withdraw_access() and revocable_deinit() are called,
- * even in the case of an early exit.
- *
- * It creates a local variable in the current scope. @_res is populated with
- * the result of revocable_try_access(). The consumer code **must** check if
- * @_res is ``NULL`` before using it. The revocable_withdraw_access() function
- * is automatically called when the scope is exited.
- *
- * Note: It shares the same issue with guard() in cleanup.h. No goto statements
- * are allowed before the helper. Otherwise, the compiler fails with
- * "jump bypasses initialization of variable with __attribute__((cleanup))".
- */
-#define REVOCABLE_TRY_ACCESS_WITH(_rp, _res) \
- _REVOCABLE_TRY_ACCESS_WITH(_rp, __UNIQUE_ID(name), _res)
-
-#define _REVOCABLE_TRY_ACCESS_SCOPED(_rp, _rev, _label, _res) \
- for (struct revocable _rev = {.rp = NULL, .idx = -1}, \
- *__UNIQUE_ID(name) __free(access_rev) = &_rev; \
- (_res = revocable_init(_rp, &_rev) ? NULL : \
- revocable_try_access(&_rev)) || true; \
- ({ goto _label; })) \
- if (0) { \
-_label: \
- break; \
- } else
-
-/**
- * REVOCABLE_TRY_ACCESS_SCOPED() - A helper for accessing revocable resource
- * @_rp: The provider's ``struct revocable_provider *`` handle.
- * @_res: A pointer variable that will be assigned the resource.
- *
- * Similar to REVOCABLE_TRY_ACCESS_WITH() but with an explicit scope from a
- * temporary ``for`` loop.
- */
-#define REVOCABLE_TRY_ACCESS_SCOPED(_rp, _res) \
- _REVOCABLE_TRY_ACCESS_SCOPED(_rp, __UNIQUE_ID(name), \
- __UNIQUE_ID(label), _res)
-
-#endif /* __LINUX_REVOCABLE_H */