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: Revocable resource management

Some resources can be removed asynchronously, for example, resources
provided by a hot-pluggable device like USB. When holding a reference
to such a resource, it's possible for the resource to be removed and
its memory freed, leading to use-after-free errors on subsequent access.

The "revocable" mechanism addresses this by establishing a weak reference
to a resource that might be freed at any time. It allows a resource
consumer to safely attempt to access the resource, guaranteeing that the
access is valid for the duration of its use, or it fails safely if the
resource has already been revoked.

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.

Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Link: https://patch.msgid.link/20260116080235.350305-2-tzungbi@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tzung-Bi Shih and committed by
Greg Kroah-Hartman
62eb5575 303db924

+472 -1
+1
Documentation/driver-api/driver-model/index.rst
··· 14 14 overview 15 15 platform 16 16 porting 17 + revocable 17 18 18 19 .. only:: subproject and html 19 20
+152
Documentation/driver-api/driver-model/revocable.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ============================== 4 + Revocable Resource Management 5 + ============================== 6 + 7 + Overview 8 + ======== 9 + 10 + .. kernel-doc:: drivers/base/revocable.c 11 + :doc: Overview 12 + 13 + Revocable vs. Devres (devm) 14 + =========================== 15 + 16 + It's important to understand the distinct roles of the Revocable and Devres, 17 + and how they can complement each other. They address different problems in 18 + resource management: 19 + 20 + * **Devres:** Primarily address **resource leaks**. The lifetime of the 21 + resources is tied to the lifetime of the device. The resource is 22 + automatically freed when the device is unbound. This cleanup happens 23 + irrespective of any potential active users. 24 + 25 + * **Revocable:** Primarily addresses **invalid memory access**, 26 + such as Use-After-Free (UAF). It's an independent synchronization 27 + primitive that decouples consumer access from the resource's actual 28 + presence. Consumers interact with a "revocable object" (an intermediary), 29 + not the underlying resource directly. This revocable object persists as 30 + long as there are active references to it from consumer handles. 31 + 32 + **Key Distinctions & How They Complement Each Other:** 33 + 34 + 1. **Reference Target:** Consumers of a resource managed by the Revocable 35 + mechanism hold a reference to the *revocable object*, not the 36 + encapsulated resource itself. 37 + 38 + 2. **Resource Lifetime vs. Access:** The underlying resource's lifetime is 39 + independent of the number of references to the revocable object. The 40 + resource can be freed at any point. A common scenario is the resource 41 + being freed by `devres` when the providing device is unbound. 42 + 43 + 3. **Safe Access:** Revocable provides a safe way to attempt access. Before 44 + using the resource, a consumer uses the Revocable API (e.g., 45 + revocable_try_access()). This function checks if the resource is still 46 + valid. It returns a pointer to the resource only if it hasn't been 47 + revoked; otherwise, it returns NULL. This prevents UAF by providing a 48 + clear signal that the resource is gone. 49 + 50 + 4. **Complementary Usage:** `devres` and Revocable work well together. 51 + `devres` can handle the automatic allocation and deallocation of a 52 + resource tied to a device. The Revocable mechanism can be layered on top 53 + to provide safe access for consumers whose lifetimes might extend beyond 54 + the provider device's lifetime. For instance, a userspace program might 55 + keep a character device file open even after the physical device has been 56 + removed. In this case: 57 + 58 + * `devres` frees the device-specific resource upon unbinding. 59 + * The Revocable mechanism ensures that any subsequent operations on the 60 + open file handle, which attempt to access the now-freed resource, 61 + will fail gracefully (e.g., revocable_try_access() returns NULL) 62 + instead of causing a UAF. 63 + 64 + In summary, `devres` ensures resources are *released* to prevent leaks, while 65 + the Revocable mechanism ensures that attempts to *access* these resources are 66 + done safely, even if the resource has been released. 67 + 68 + API and Usage 69 + ============= 70 + 71 + For Resource Providers 72 + ---------------------- 73 + .. kernel-doc:: drivers/base/revocable.c 74 + :identifiers: revocable_provider 75 + 76 + .. kernel-doc:: drivers/base/revocable.c 77 + :identifiers: revocable_provider_alloc 78 + 79 + .. kernel-doc:: drivers/base/revocable.c 80 + :identifiers: devm_revocable_provider_alloc 81 + 82 + .. kernel-doc:: drivers/base/revocable.c 83 + :identifiers: revocable_provider_revoke 84 + 85 + For Resource Consumers 86 + ---------------------- 87 + .. kernel-doc:: drivers/base/revocable.c 88 + :identifiers: revocable 89 + 90 + .. kernel-doc:: drivers/base/revocable.c 91 + :identifiers: revocable_alloc 92 + 93 + .. kernel-doc:: drivers/base/revocable.c 94 + :identifiers: revocable_free 95 + 96 + .. kernel-doc:: drivers/base/revocable.c 97 + :identifiers: revocable_try_access 98 + 99 + .. kernel-doc:: drivers/base/revocable.c 100 + :identifiers: revocable_withdraw_access 101 + 102 + .. kernel-doc:: include/linux/revocable.h 103 + :identifiers: REVOCABLE_TRY_ACCESS_WITH 104 + 105 + Example Usage 106 + ~~~~~~~~~~~~~ 107 + 108 + .. code-block:: c 109 + 110 + void consumer_use_resource(struct revocable *rev) 111 + { 112 + struct foo_resource *res; 113 + 114 + REVOCABLE_TRY_ACCESS_WITH(rev, res); 115 + // Always check if the resource is valid. 116 + if (!res) { 117 + pr_warn("Resource is not available\n"); 118 + return; 119 + } 120 + 121 + // At this point, 'res' is guaranteed to be valid until 122 + // this block exits. 123 + do_something_with(res); 124 + 125 + } // revocable_withdraw_access() is automatically called here. 126 + 127 + .. kernel-doc:: include/linux/revocable.h 128 + :identifiers: REVOCABLE_TRY_ACCESS_SCOPED 129 + 130 + Example Usage 131 + ~~~~~~~~~~~~~ 132 + 133 + .. code-block:: c 134 + 135 + void consumer_use_resource(struct revocable *rev) 136 + { 137 + struct foo_resource *res; 138 + 139 + REVOCABLE_TRY_ACCESS_SCOPED(rev, res) { 140 + // Always check if the resource is valid. 141 + if (!res) { 142 + pr_warn("Resource is not available\n"); 143 + return; 144 + } 145 + 146 + // At this point, 'res' is guaranteed to be valid until 147 + // this block exits. 148 + do_something_with(res); 149 + } 150 + 151 + // revocable_withdraw_access() is automatically called here. 152 + }
+7
MAINTAINERS
··· 22368 22368 F: kernel/rseq.c 22369 22369 F: tools/testing/selftests/rseq/ 22370 22370 22371 + REVOCABLE RESOURCE MANAGEMENT 22372 + M: Tzung-Bi Shih <tzungbi@kernel.org> 22373 + L: linux-kernel@vger.kernel.org 22374 + S: Maintained 22375 + F: drivers/base/revocable.c 22376 + F: include/linux/revocable.h 22377 + 22371 22378 RFKILL 22372 22379 M: Johannes Berg <johannes@sipsolutions.net> 22373 22380 L: linux-wireless@vger.kernel.org
+1 -1
drivers/base/Makefile
··· 6 6 cpu.o firmware.o init.o map.o devres.o \ 7 7 attribute_container.o transport_class.o \ 8 8 topology.o container.o property.o cacheinfo.o \ 9 - swnode.o faux.o 9 + swnode.o faux.o revocable.o 10 10 obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o 11 11 obj-$(CONFIG_DEVTMPFS) += devtmpfs.o 12 12 obj-y += power/
+242
drivers/base/revocable.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2026 Google LLC 4 + * 5 + * Revocable resource management 6 + */ 7 + 8 + #include <linux/device.h> 9 + #include <linux/kref.h> 10 + #include <linux/revocable.h> 11 + #include <linux/slab.h> 12 + #include <linux/srcu.h> 13 + 14 + /** 15 + * DOC: Overview 16 + * 17 + * The "revocable" mechanism is a synchronization primitive designed to manage 18 + * safe access to resources that can be asynchronously removed or invalidated. 19 + * Its primary purpose is to prevent Use-After-Free (UAF) errors when 20 + * interacting with resources whose lifetimes are not guaranteed to outlast 21 + * their consumers. 22 + * 23 + * This is particularly useful in systems where resources can disappear 24 + * unexpectedly, such as those provided by hot-pluggable devices like USB. 25 + * When a consumer holds a reference to such a resource, the underlying device 26 + * might be removed, causing the resource's memory to be freed. Subsequent 27 + * access attempts by the consumer would then lead to UAF errors. 28 + * 29 + * Revocable addresses this by providing a form of "weak reference" and a 30 + * controlled access method. It allows a resource consumer to safely attempt to 31 + * access the resource. The mechanism guarantees that any access granted is 32 + * valid for the duration of its use. If the resource has already been 33 + * revoked (i.e., freed), the access attempt will fail safely, typically by 34 + * returning NULL, instead of causing a crash. 35 + * 36 + * The implementation uses a provider/consumer model built on Sleepable 37 + * RCU (SRCU) to guarantee safe memory access: 38 + * 39 + * - A resource provider, such as a driver for a hot-pluggable device, 40 + * allocates a struct revocable_provider and initializes it with a pointer 41 + * to the resource. 42 + * 43 + * - A resource consumer that wants to access the resource allocates a 44 + * struct revocable which acts as a handle containing a reference to the 45 + * provider. 46 + * 47 + * - To access the resource, the consumer uses revocable_try_access(). 48 + * This function enters an SRCU read-side critical section and returns 49 + * the pointer to the resource. If the provider has already freed the 50 + * resource, it returns NULL. After use, the consumer calls 51 + * revocable_withdraw_access() to exit the SRCU critical section. The 52 + * REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED() are 53 + * convenient helpers for doing that. 54 + * 55 + * - When the provider needs to remove the resource, it calls 56 + * revocable_provider_revoke(). This function sets the internal resource 57 + * pointer to NULL and then calls synchronize_srcu() to wait for all 58 + * current readers to finish before the resource can be completely torn 59 + * down. 60 + */ 61 + 62 + /** 63 + * struct revocable_provider - A handle for resource provider. 64 + * @srcu: The SRCU to protect the resource. 65 + * @res: The pointer of resource. It can point to anything. 66 + * @kref: The refcount for this handle. 67 + */ 68 + struct revocable_provider { 69 + struct srcu_struct srcu; 70 + void __rcu *res; 71 + struct kref kref; 72 + }; 73 + 74 + /** 75 + * struct revocable - A handle for resource consumer. 76 + * @rp: The pointer of resource provider. 77 + * @idx: The index for the RCU critical section. 78 + */ 79 + struct revocable { 80 + struct revocable_provider *rp; 81 + int idx; 82 + }; 83 + 84 + /** 85 + * revocable_provider_alloc() - Allocate struct revocable_provider. 86 + * @res: The pointer of resource. 87 + * 88 + * This holds an initial refcount to the struct. 89 + * 90 + * Return: The pointer of struct revocable_provider. NULL on errors. 91 + */ 92 + struct revocable_provider *revocable_provider_alloc(void *res) 93 + { 94 + struct revocable_provider *rp; 95 + 96 + rp = kzalloc(sizeof(*rp), GFP_KERNEL); 97 + if (!rp) 98 + return NULL; 99 + 100 + init_srcu_struct(&rp->srcu); 101 + rcu_assign_pointer(rp->res, res); 102 + synchronize_srcu(&rp->srcu); 103 + kref_init(&rp->kref); 104 + 105 + return rp; 106 + } 107 + EXPORT_SYMBOL_GPL(revocable_provider_alloc); 108 + 109 + static void revocable_provider_release(struct kref *kref) 110 + { 111 + struct revocable_provider *rp = container_of(kref, 112 + struct revocable_provider, kref); 113 + 114 + cleanup_srcu_struct(&rp->srcu); 115 + kfree(rp); 116 + } 117 + 118 + /** 119 + * revocable_provider_revoke() - Revoke the managed resource. 120 + * @rp: The pointer of resource provider. 121 + * 122 + * This sets the resource `(struct revocable_provider *)->res` to NULL to 123 + * indicate the resource has gone. 124 + * 125 + * This drops the refcount to the resource provider. If it is the final 126 + * reference, revocable_provider_release() will be called to free the struct. 127 + */ 128 + void revocable_provider_revoke(struct revocable_provider *rp) 129 + { 130 + rcu_assign_pointer(rp->res, NULL); 131 + synchronize_srcu(&rp->srcu); 132 + kref_put(&rp->kref, revocable_provider_release); 133 + } 134 + EXPORT_SYMBOL_GPL(revocable_provider_revoke); 135 + 136 + static void devm_revocable_provider_revoke(void *data) 137 + { 138 + struct revocable_provider *rp = data; 139 + 140 + revocable_provider_revoke(rp); 141 + } 142 + 143 + /** 144 + * devm_revocable_provider_alloc() - Dev-managed revocable_provider_alloc(). 145 + * @dev: The device. 146 + * @res: The pointer of resource. 147 + * 148 + * It is convenient to allocate providers via this function if the @res is 149 + * also tied to the lifetime of the @dev. revocable_provider_revoke() will 150 + * be called automatically when the device is unbound. 151 + * 152 + * This holds an initial refcount to the struct. 153 + * 154 + * Return: The pointer of struct revocable_provider. NULL on errors. 155 + */ 156 + struct revocable_provider *devm_revocable_provider_alloc(struct device *dev, 157 + void *res) 158 + { 159 + struct revocable_provider *rp; 160 + 161 + rp = revocable_provider_alloc(res); 162 + if (!rp) 163 + return NULL; 164 + 165 + if (devm_add_action_or_reset(dev, devm_revocable_provider_revoke, rp)) 166 + return NULL; 167 + 168 + return rp; 169 + } 170 + EXPORT_SYMBOL_GPL(devm_revocable_provider_alloc); 171 + 172 + /** 173 + * revocable_alloc() - Allocate struct revocable. 174 + * @rp: The pointer of resource provider. 175 + * 176 + * This holds a refcount to the resource provider. 177 + * 178 + * Return: The pointer of struct revocable. NULL on errors. 179 + */ 180 + struct revocable *revocable_alloc(struct revocable_provider *rp) 181 + { 182 + struct revocable *rev; 183 + 184 + rev = kzalloc(sizeof(*rev), GFP_KERNEL); 185 + if (!rev) 186 + return NULL; 187 + 188 + rev->rp = rp; 189 + kref_get(&rp->kref); 190 + 191 + return rev; 192 + } 193 + EXPORT_SYMBOL_GPL(revocable_alloc); 194 + 195 + /** 196 + * revocable_free() - Free struct revocable. 197 + * @rev: The pointer of struct revocable. 198 + * 199 + * This drops a refcount to the resource provider. If it is the final 200 + * reference, revocable_provider_release() will be called to free the struct. 201 + */ 202 + void revocable_free(struct revocable *rev) 203 + { 204 + struct revocable_provider *rp = rev->rp; 205 + 206 + kref_put(&rp->kref, revocable_provider_release); 207 + kfree(rev); 208 + } 209 + EXPORT_SYMBOL_GPL(revocable_free); 210 + 211 + /** 212 + * revocable_try_access() - Try to access the resource. 213 + * @rev: The pointer of struct revocable. 214 + * 215 + * This tries to de-reference to the resource and enters a RCU critical 216 + * section. 217 + * 218 + * Return: The pointer to the resource. NULL if the resource has gone. 219 + */ 220 + void *revocable_try_access(struct revocable *rev) __acquires(&rev->rp->srcu) 221 + { 222 + struct revocable_provider *rp = rev->rp; 223 + 224 + rev->idx = srcu_read_lock(&rp->srcu); 225 + return srcu_dereference(rp->res, &rp->srcu); 226 + } 227 + EXPORT_SYMBOL_GPL(revocable_try_access); 228 + 229 + /** 230 + * revocable_withdraw_access() - Stop accessing to the resource. 231 + * @rev: The pointer of struct revocable. 232 + * 233 + * Call this function to indicate the resource is no longer used. It exits 234 + * the RCU critical section. 235 + */ 236 + void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp->srcu) 237 + { 238 + struct revocable_provider *rp = rev->rp; 239 + 240 + srcu_read_unlock(&rp->srcu, rev->idx); 241 + } 242 + EXPORT_SYMBOL_GPL(revocable_withdraw_access);
+69
include/linux/revocable.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright 2026 Google LLC 4 + */ 5 + 6 + #ifndef __LINUX_REVOCABLE_H 7 + #define __LINUX_REVOCABLE_H 8 + 9 + #include <linux/compiler.h> 10 + #include <linux/cleanup.h> 11 + 12 + struct device; 13 + struct revocable; 14 + struct revocable_provider; 15 + 16 + struct revocable_provider *revocable_provider_alloc(void *res); 17 + void revocable_provider_revoke(struct revocable_provider *rp); 18 + struct revocable_provider *devm_revocable_provider_alloc(struct device *dev, 19 + void *res); 20 + 21 + struct revocable *revocable_alloc(struct revocable_provider *rp); 22 + void revocable_free(struct revocable *rev); 23 + void *revocable_try_access(struct revocable *rev) __acquires(&rev->rp->srcu); 24 + void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp->srcu); 25 + 26 + DEFINE_FREE(access_rev, struct revocable *, if (_T) revocable_withdraw_access(_T)) 27 + 28 + /** 29 + * REVOCABLE_TRY_ACCESS_WITH() - A helper for accessing revocable resource 30 + * @_rev: The consumer's ``struct revocable *`` handle. 31 + * @_res: A pointer variable that will be assigned the resource. 32 + * 33 + * The macro simplifies the access-release cycle for consumers, ensuring that 34 + * revocable_withdraw_access() is always called, even in the case of an early 35 + * exit. 36 + * 37 + * It creates a local variable in the current scope. @_res is populated with 38 + * the result of revocable_try_access(). The consumer code **must** check if 39 + * @_res is ``NULL`` before using it. The revocable_withdraw_access() function 40 + * is automatically called when the scope is exited. 41 + * 42 + * Note: It shares the same issue with guard() in cleanup.h. No goto statements 43 + * are allowed before the helper. Otherwise, the compiler fails with 44 + * "jump bypasses initialization of variable with __attribute__((cleanup))". 45 + */ 46 + #define REVOCABLE_TRY_ACCESS_WITH(_rev, _res) \ 47 + struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \ 48 + _res = revocable_try_access(_rev) 49 + 50 + #define _REVOCABLE_TRY_ACCESS_SCOPED(_rev, _label, _res) \ 51 + for (struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \ 52 + (_res = revocable_try_access(_rev)) || true; ({ goto _label; })) \ 53 + if (0) { \ 54 + _label: \ 55 + break; \ 56 + } else 57 + 58 + /** 59 + * REVOCABLE_TRY_ACCESS_SCOPED() - A helper for accessing revocable resource 60 + * @_rev: The consumer's ``struct revocable *`` handle. 61 + * @_res: A pointer variable that will be assigned the resource. 62 + * 63 + * Similar to REVOCABLE_TRY_ACCESS_WITH() but with an explicit scope from a 64 + * temporary ``for`` loop. 65 + */ 66 + #define REVOCABLE_TRY_ACCESS_SCOPED(_rev, _res) \ 67 + _REVOCABLE_TRY_ACCESS_SCOPED(_rev, __UNIQUE_ID(label), _res) 68 + 69 + #endif /* __LINUX_REVOCABLE_H */