summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorTzung-Bi Shih <tzungbi@kernel.org>2026-01-16 08:02:33 +0000
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-01-16 16:16:51 +0100
commit62eb557580eb2177cf16c3fd2b6efadff297b29a (patch)
treed406c7017ccb2566ad28d6311b8fb73dfe629dde /include
parent303db924fe0bb298242694c0c36fa1890cc9cf33 (diff)
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>
Diffstat (limited to 'include')
-rw-r--r--include/linux/revocable.h69
1 files changed, 69 insertions, 0 deletions
diff --git a/include/linux/revocable.h b/include/linux/revocable.h
new file mode 100644
index 000000000000..659ba01c58db
--- /dev/null
+++ b/include/linux/revocable.h
@@ -0,0 +1,69 @@
+/* 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;
+struct revocable_provider;
+
+struct revocable_provider *revocable_provider_alloc(void *res);
+void revocable_provider_revoke(struct revocable_provider *rp);
+struct revocable_provider *devm_revocable_provider_alloc(struct device *dev,
+ void *res);
+
+struct revocable *revocable_alloc(struct revocable_provider *rp);
+void revocable_free(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) revocable_withdraw_access(_T))
+
+/**
+ * REVOCABLE_TRY_ACCESS_WITH() - A helper for accessing revocable resource
+ * @_rev: The consumer's ``struct revocable *`` handle.
+ * @_res: A pointer variable that will be assigned the resource.
+ *
+ * The macro simplifies the access-release cycle for consumers, ensuring that
+ * revocable_withdraw_access() is always 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(_rev, _res) \
+ struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \
+ _res = revocable_try_access(_rev)
+
+#define _REVOCABLE_TRY_ACCESS_SCOPED(_rev, _label, _res) \
+ for (struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \
+ (_res = revocable_try_access(_rev)) || true; ({ goto _label; })) \
+ if (0) { \
+_label: \
+ break; \
+ } else
+
+/**
+ * REVOCABLE_TRY_ACCESS_SCOPED() - A helper for accessing revocable resource
+ * @_rev: The consumer's ``struct revocable *`` 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(_rev, _res) \
+ _REVOCABLE_TRY_ACCESS_SCOPED(_rev, __UNIQUE_ID(label), _res)
+
+#endif /* __LINUX_REVOCABLE_H */