From nobody Wed Dec 17 14:36:47 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 862BF23D283; Thu, 16 Oct 2025 05:42:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593375; cv=none; b=Mp2H4JVLOi1ylRw6zmTYsQCFhhOfr6poi6V9BKibsWMdaIHMAguHNbdAYejrcC+mRoum6RTbeN2wXU+0m7T11kIDXMljSkFWWdhV6Ep4wVx+9zC3KPPH1Ccuh9512UrP2RhRQTdBhUJDm1KSJcPyOX0zVNqPHVOoxMxwJC84+BQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593375; c=relaxed/simple; bh=K4qmDGrhCpbipBnlfFqcIp+TTyDw70vxxVANLjpgm54=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cnUbhv7pikcH4vDW/9SOCN2UcxLuN+zPe0EeAvR3P2p57HImRjGqsT9W3N07J4aqHtpxJzDzutgrbmNRjpBlUG7EJYbpAn2H5WpJNK4e0XjnDUIF/w7Gc2A0T8ym5Inx+UK2gWx0fq+vVRSs34XknxR4lGKJSN9nHy7M+UP0nNs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jrdZ+8UC; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jrdZ+8UC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A3F67C116B1; Thu, 16 Oct 2025 05:42:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760593375; bh=K4qmDGrhCpbipBnlfFqcIp+TTyDw70vxxVANLjpgm54=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jrdZ+8UCWleAeGJBPn9btmNbGAhkJOQNXwtkqzRiVsaNZPSvSfc0W8Erv8Ub0oMVa ETbvwGk1Db9efOdemYuxems/JjNFzBJzCvgvuka0Xdrc6rp7ANZM2uKelhPgq3PCis QqETMgA3W+jPL6T5DZgLaux7+eOdDv0aKb1jklVm6v9Eg8oO3KbAKbJG9H7FWcyAEN 8bPGqGtBt0b6bPHEXT3r+R7k0MsBImjvpvKQzdFWtbkvxFxQyN5tw8zanm4HvPWQ4Y NXnirzJv1rwtM8Ad0+ueNdgrFoT91U5tpdDDDs/2HtOAL9wLlv+PUkyP9uKJvLbmRB 73orR4ezIRiEw== From: Tzung-Bi Shih To: Benson Leung , Greg Kroah-Hartman , "Rafael J . Wysocki" , Danilo Krummrich Cc: Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, tzungbi@kernel.org, Laurent Pinchart , Bartosz Golaszewski , Wolfram Sang , Simona Vetter , Dan Williams , Jason Gunthorpe Subject: [PATCH v5 1/7] revocable: Revocable resource management Date: Thu, 16 Oct 2025 05:41:58 +0000 Message-ID: <20251016054204.1523139-2-tzungbi@kernel.org> X-Mailer: git-send-email 2.51.0.788.g6d19910ace-goog In-Reply-To: <20251016054204.1523139-1-tzungbi@kernel.org> References: <20251016054204.1523139-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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() is a convenient helper 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 Acked-by: Simona Vetter Signed-off-by: Tzung-Bi Shih --- v5: - No changes. v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-2-tzungbi= @kernel.org - Rename: - revocable_provider_free() -> revocable_provider_revoke(). - REVOCABLE() -> REVOCABLE_TRY_ACCESS_WITH(). - revocable_release() -> revocable_withdraw_access(). - rcu_dereference() -> srcu_dereference() to fix a warning from lock debugg= ing. - Move most docs to kernel-doc, include them in Documentation/, and modify = the commit message accordingly. - Fix some doc errors. - Add Acked-by tags. v3: https://lore.kernel.org/chrome-platform/20250912081718.3827390-2-tzungb= i@kernel.org - No changes. v2: https://lore.kernel.org/chrome-platform/20250820081645.847919-2-tzungbi= @kernel.org - Rename "ref_proxy" -> "revocable". - Add introduction in kernel-doc format in revocable.c. - Add MAINTAINERS entry. - Add copyright. - Move from lib/ to drivers/base/. - EXPORT_SYMBOL() -> EXPORT_SYMBOL_GPL(). - Add Documentation/. - Rename _get() -> try_access(); _put() -> release(). - Fix a sparse warning by removing the redundant __rcu annotations. - Fix a sparse warning by adding __acquires() and __releases() annotations. v1: https://lore.kernel.org/chrome-platform/20250814091020.1302888-2-tzungb= i@kernel.org Note (for my reference): - An optional .release() callback for revocable provider-managed resource hasn't added. - `make O=3Dbuild SPHINXDIRS=3Ddriver-api/driver-model/ htmldocs` a way to verify the Documentation/. .../driver-api/driver-model/index.rst | 1 + .../driver-api/driver-model/revocable.rst | 87 +++++++ MAINTAINERS | 7 + drivers/base/Makefile | 2 +- drivers/base/revocable.c | 233 ++++++++++++++++++ include/linux/revocable.h | 53 ++++ 6 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 Documentation/driver-api/driver-model/revocable.rst create mode 100644 drivers/base/revocable.c create mode 100644 include/linux/revocable.h diff --git a/Documentation/driver-api/driver-model/index.rst b/Documentatio= n/driver-api/driver-model/index.rst index 4831bdd92e5c..8e1ee21185df 100644 --- a/Documentation/driver-api/driver-model/index.rst +++ b/Documentation/driver-api/driver-model/index.rst @@ -14,6 +14,7 @@ Driver Model overview platform porting + revocable =20 .. only:: subproject and html =20 diff --git a/Documentation/driver-api/driver-model/revocable.rst b/Document= ation/driver-api/driver-model/revocable.rst new file mode 100644 index 000000000000..ce2d0eb1c8cb --- /dev/null +++ b/Documentation/driver-api/driver-model/revocable.rst @@ -0,0 +1,87 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D +Revocable Resource Management +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + +Overview +=3D=3D=3D=3D=3D=3D=3D=3D + +.. kernel-doc:: drivers/base/revocable.c + :doc: Overview + +Revocable vs. Device-Managed (devm) Resources +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +It's important to understand the distinction between a standard +device-managed (devm) resource and a resource managed by a revocable provi= der. + +The key difference is their lifetime: + +* A **devm resource** is tied to the lifetime of the device. It is + automatically freed when the device is unbound. +* A **revocable provider** persists as long as there are active referenc= es + to it from consumer handles. + +This means that a revocable provider can outlive the device that created +it. This is a deliberate design feature, allowing consumers to hold a +reference to a resource even after the underlying device has been removed, +without causing a fault. When the consumer attempts to access the resourc= e, +it will simply be informed that the resource is no longer available. + +API and Usage +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +For Resource Providers +---------------------- + +.. kernel-doc:: drivers/base/revocable.c + :identifiers: revocable_provider_alloc + +.. kernel-doc:: drivers/base/revocable.c + :identifiers: devm_revocable_provider_alloc + +.. kernel-doc:: drivers/base/revocable.c + :identifiers: revocable_provider_revoke + +For Resource Consumers +---------------------- + +.. kernel-doc:: drivers/base/revocable.c + :identifiers: revocable_alloc + +.. kernel-doc:: drivers/base/revocable.c + :identifiers: revocable_free + +.. 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 *rev) + { + struct foo_resource *res; + + REVOCABLE_TRY_ACCESS_WITH(rev, 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 25ed1846d970..7d00ff431af9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22020,6 +22020,13 @@ F: include/uapi/linux/rseq.h F: kernel/rseq.c F: tools/testing/selftests/rseq/ =20 +REVOCABLE RESOURCE MANAGEMENT +M: Tzung-Bi Shih +L: linux-kernel@vger.kernel.org +S: Maintained +F: drivers/base/revocable.c +F: include/linux/revocable.h + RFKILL M: Johannes Berg L: linux-wireless@vger.kernel.org diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 8074a10183dc..bdf854694e39 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -6,7 +6,7 @@ obj-y :=3D component.o core.o bus.o dd.o syscore.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ topology.o container.o property.o cacheinfo.o \ - swnode.o faux.o + swnode.o faux.o revocable.o obj-$(CONFIG_AUXILIARY_BUS) +=3D auxiliary.o obj-$(CONFIG_DEVTMPFS) +=3D devtmpfs.o obj-y +=3D power/ diff --git a/drivers/base/revocable.c b/drivers/base/revocable.c new file mode 100644 index 000000000000..f8dd4363a87b --- /dev/null +++ b/drivers/base/revocable.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 Google LLC + * + * Revocable resource management + */ + +#include +#include +#include +#include +#include + +/** + * DOC: Overview + * + * 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 referen= ce + * 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 point= er + * 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() is a convenient helper 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. + */ +struct revocable_provider { + struct srcu_struct srcu; + void __rcu *res; + struct kref kref; +}; + +/** + * 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; +}; + +/** + * 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. + */ +struct revocable_provider *revocable_provider_alloc(void *res) +{ + struct revocable_provider *rp; + + rp =3D kzalloc(sizeof(*rp), GFP_KERNEL); + if (!rp) + return NULL; + + init_srcu_struct(&rp->srcu); + rcu_assign_pointer(rp->res, res); + synchronize_srcu(&rp->srcu); + kref_init(&rp->kref); + + return rp; +} +EXPORT_SYMBOL_GPL(revocable_provider_alloc); + +static void revocable_provider_release(struct kref *kref) +{ + struct revocable_provider *rp =3D container_of(kref, + struct revocable_provider, kref); + + cleanup_srcu_struct(&rp->srcu); + kfree(rp); +} + +/** + * revocable_provider_revoke() - Revoke the managed resource. + * @rp: The 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 stru= ct. + */ +void revocable_provider_revoke(struct revocable_provider *rp) +{ + rcu_assign_pointer(rp->res, NULL); + synchronize_srcu(&rp->srcu); + kref_put(&rp->kref, revocable_provider_release); +} +EXPORT_SYMBOL_GPL(revocable_provider_revoke); + +static void devm_revocable_provider_revoke(void *data) +{ + struct revocable_provider *rp =3D data; + + revocable_provider_revoke(rp); +} + +/** + * devm_revocable_provider_alloc() - Dev-managed revocable_provider_alloc(= ). + * @dev: The device. + * @res: The pointer of resource. + * + * It is convenient to allocate providers via this function if the @res is + * also tied to the lifetime of the @dev. revocable_provider_revoke() will + * be called automatically when the device is unbound. + * + * This holds an initial refcount to the struct. + * + * Return: The pointer of struct revocable_provider. NULL on errors. + */ +struct revocable_provider *devm_revocable_provider_alloc(struct device *de= v, + void *res) +{ + struct revocable_provider *rp; + + rp =3D revocable_provider_alloc(res); + if (!rp) + return NULL; + + if (devm_add_action_or_reset(dev, devm_revocable_provider_revoke, rp)) + return NULL; + + return rp; +} +EXPORT_SYMBOL_GPL(devm_revocable_provider_alloc); + +/** + * revocable_alloc() - Allocate struct revocable. + * @rp: The pointer of resource provider. + * + * This holds a refcount to the resource provider. + * + * Return: The pointer of struct revocable. NULL on errors. + */ +struct revocable *revocable_alloc(struct revocable_provider *rp) +{ + struct revocable *rev; + + rev =3D kzalloc(sizeof(*rev), GFP_KERNEL); + if (!rev) + return NULL; + + rev->rp =3D rp; + kref_get(&rp->kref); + + return rev; +} +EXPORT_SYMBOL_GPL(revocable_alloc); + +/** + * revocable_free() - Free 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 stru= ct. + */ +void revocable_free(struct revocable *rev) +{ + struct revocable_provider *rp =3D rev->rp; + + kref_put(&rp->kref, revocable_provider_release); + kfree(rev); +} +EXPORT_SYMBOL_GPL(revocable_free); + +/** + * 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->src= u) +{ + struct revocable_provider *rp =3D rev->rp; + + rev->idx =3D 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 =3D 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 new file mode 100644 index 000000000000..7177bf045d9c --- /dev/null +++ b/include/linux/revocable.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2025 Google LLC + */ + +#ifndef __LINUX_REVOCABLE_H +#define __LINUX_REVOCABLE_H + +#include + +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 *de= v, + 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->src= u); +void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp-= >srcu); + +DEFINE_FREE(revocable, struct revocable *, if (_T) revocable_withdraw_acce= ss(_T)) + +#define _REVOCABLE_TRY_ACCESS_WITH(_rev, _label, _res) \ + for (struct revocable *__UNIQUE_ID(name) __free(revocable) =3D _rev; \ + (_res =3D revocable_try_access(_rev)) || true; ({ goto _label; })) \ + if (0) { \ +_label: \ + break; \ + } else + +/** + * 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 t= hat + * revocable_withdraw_access() is always called, even in the case of an ea= rly + * exit. + * + * It creates a ``for`` loop that executes exactly once. Inside the loop, + * @_res is populated with the result of revocable_try_access(). The cons= umer + * code **must** check if @_res is ``NULL`` before using it. The + * revocable_withdraw_access() function is automatically called when the s= cope + * of the loop is exited. + */ +#define REVOCABLE_TRY_ACCESS_WITH(_rev, _res) \ + _REVOCABLE_TRY_ACCESS_WITH(_rev, __UNIQUE_ID(label), _res) + +#endif /* __LINUX_REVOCABLE_H */ --=20 2.51.0.788.g6d19910ace-goog From nobody Wed Dec 17 14:36:47 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7A82023D7F2; Thu, 16 Oct 2025 05:42:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593378; cv=none; b=cOeO/D3QDe9S0eQRk19QjYDU4ZDz5f66wphmVydLxPlJ6/QgmEBX69xvs3HrNinSwi/4i6mDewzX9pl3+W8Q18nBlzxaH+PiZX4T4zbSp/0JSIrseKH9tIbY1HfgrjTOocaMqfdT8Xf0VUnoe39mF0eBTvrNEMVrWObcVVQJzJA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593378; c=relaxed/simple; bh=ul0VQspZhYdpEYWb1FJ2kj/iE20/cXl7bZTbEN6NO2w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KPU11XC3CRqntRxs/kbIbYI35dxFEcP1gGuSNU0GSlxO8HANzW3trHTqIF3BvJUeRNWzDYPTJUj8zgd4kjqjMgo0wYgACA2dtHRYbwRVhBrtIxGD+ium3GGjJtmQ0VAr/ladGSamAu5VPBj0QdrMhoVi5F/qcCExq7gki2CitCw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cNdC3TDw; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cNdC3TDw" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7D59EC4CEFE; Thu, 16 Oct 2025 05:42:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760593378; bh=ul0VQspZhYdpEYWb1FJ2kj/iE20/cXl7bZTbEN6NO2w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cNdC3TDwm+MzVUM9hW9cW01rpKoWrjYOZ5+GdaIPoncDzNfwgelneWQxqzBOXtp7b DHLuDMmVkEW8dz36bUlDLT/D3RGMHo31CUrULcbxSJZLHh2f291AjiP0JhXjAEvyxM JEpfoGjXoldR7Bnwxb7HPfIuW4kMP4a2pEHvEHpO5G+ifpsaf069xGkKlmNwGfvmRq /3Obl+y8gOoUbcnmThVq5upAWM7B+95lL9xh70QItWVad16S69uyesF7BRiawGatiV cMWs+HVZJbFS0pTH2zH92P+lajoPBduGuDKKI45FMlmsG2/0UTkGs7GShmF1ptT8Sa gVkf+SmYSiTjQ== From: Tzung-Bi Shih To: Benson Leung , Greg Kroah-Hartman , "Rafael J . Wysocki" , Danilo Krummrich Cc: Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, tzungbi@kernel.org, Laurent Pinchart , Bartosz Golaszewski , Wolfram Sang , Simona Vetter , Dan Williams , Jason Gunthorpe Subject: [PATCH v5 2/7] revocable: Add Kunit test cases Date: Thu, 16 Oct 2025 05:41:59 +0000 Message-ID: <20251016054204.1523139-3-tzungbi@kernel.org> X-Mailer: git-send-email 2.51.0.788.g6d19910ace-goog In-Reply-To: <20251016054204.1523139-1-tzungbi@kernel.org> References: <20251016054204.1523139-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add Kunit test cases for the revocable API. The test cases cover the following scenarios: - Basic: Verifies that a consumer can successfully access the resource provided via the provider. - Revocation: Verifies that after the provider revokes the resource, the consumer correctly receives a NULL pointer on a subsequent access. - Macro: Same as "Revocation" but uses the REVOCABLE(). A way to run the test: $ ./tools/testing/kunit/kunit.py run \ --kconfig_add CONFIG_REVOCABLE_KUNIT_TEST=3Dy \ revocable_test Signed-off-by: Tzung-Bi Shih --- v5: - No changes. v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-3-tzungbi= @kernel.org - REVOCABLE() -> REVOCABLE_TRY_ACCESS_WITH(). - revocable_release() -> revocable_withdraw_access(). v3: https://lore.kernel.org/chrome-platform/20250912081718.3827390-3-tzungb= i@kernel.org - No changes. v2: https://lore.kernel.org/chrome-platform/20250820081645.847919-3-tzungbi= @kernel.org - New in the series. MAINTAINERS | 1 + drivers/base/Kconfig | 8 +++ drivers/base/Makefile | 3 + drivers/base/revocable_test.c | 110 ++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+) create mode 100644 drivers/base/revocable_test.c diff --git a/MAINTAINERS b/MAINTAINERS index 7d00ff431af9..7207538f31f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22025,6 +22025,7 @@ M: Tzung-Bi Shih L: linux-kernel@vger.kernel.org S: Maintained F: drivers/base/revocable.c +F: drivers/base/revocable_test.c F: include/linux/revocable.h =20 RFKILL diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 1786d87b29e2..8f7d7b9d81ac 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -250,3 +250,11 @@ config FW_DEVLINK_SYNC_STATE_TIMEOUT work on. =20 endmenu + +# Kunit test cases +config REVOCABLE_KUNIT_TEST + tristate "Kunit tests for revocable" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Kunit tests for the revocable API. diff --git a/drivers/base/Makefile b/drivers/base/Makefile index bdf854694e39..4185aaa9bbb9 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -35,3 +35,6 @@ ccflags-$(CONFIG_DEBUG_DRIVER) :=3D -DDEBUG # define_trace.h needs to know how to find our header CFLAGS_trace.o :=3D -I$(src) obj-$(CONFIG_TRACING) +=3D trace.o + +# Kunit test cases +obj-$(CONFIG_REVOCABLE_KUNIT_TEST) +=3D revocable_test.o diff --git a/drivers/base/revocable_test.c b/drivers/base/revocable_test.c new file mode 100644 index 000000000000..1c3e08ba0505 --- /dev/null +++ b/drivers/base/revocable_test.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 Google LLC + * + * Kunit tests for the revocable API. + * + * The test cases cover the following scenarios: + * + * - Basic: Verifies that a consumer can successfully access the resource + * provided via the provider. + * + * - Revocation: Verifies that after the provider revokes the resource, + * the consumer correctly receives a NULL pointer on a subsequent access. + * + * - Macro: Same as "Revocation" but uses the REVOCABLE_TRY_ACCESS_WITH(). + */ + +#include +#include + +static void revocable_test_basic(struct kunit *test) +{ + struct revocable_provider *rp; + struct revocable *rev; + void *real_res =3D (void *)0x12345678, *res; + + rp =3D revocable_provider_alloc(real_res); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp); + + rev =3D revocable_alloc(rp); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rev); + + res =3D revocable_try_access(rev); + KUNIT_EXPECT_PTR_EQ(test, res, real_res); + revocable_withdraw_access(rev); + + revocable_free(rev); + revocable_provider_revoke(rp); +} + +static void revocable_test_revocation(struct kunit *test) +{ + struct revocable_provider *rp; + struct revocable *rev; + void *real_res =3D (void *)0x12345678, *res; + + rp =3D revocable_provider_alloc(real_res); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp); + + rev =3D revocable_alloc(rp); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rev); + + res =3D revocable_try_access(rev); + KUNIT_EXPECT_PTR_EQ(test, res, real_res); + revocable_withdraw_access(rev); + + revocable_provider_revoke(rp); + + res =3D revocable_try_access(rev); + KUNIT_EXPECT_PTR_EQ(test, res, NULL); + revocable_withdraw_access(rev); + + revocable_free(rev); +} + +static void revocable_test_macro(struct kunit *test) +{ + struct revocable_provider *rp; + struct revocable *rev; + void *real_res =3D (void *)0x12345678, *res; + bool accessed; + + rp =3D revocable_provider_alloc(real_res); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp); + + rev =3D revocable_alloc(rp); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rev); + + accessed =3D false; + REVOCABLE_TRY_ACCESS_WITH(rev, res) { + KUNIT_EXPECT_PTR_EQ(test, res, real_res); + accessed =3D true; + } + KUNIT_EXPECT_TRUE(test, accessed); + + revocable_provider_revoke(rp); + + accessed =3D false; + REVOCABLE_TRY_ACCESS_WITH(rev, res) { + KUNIT_EXPECT_PTR_EQ(test, res, NULL); + accessed =3D true; + } + KUNIT_EXPECT_TRUE(test, accessed); + + revocable_free(rev); +} + +static struct kunit_case revocable_test_cases[] =3D { + KUNIT_CASE(revocable_test_basic), + KUNIT_CASE(revocable_test_revocation), + KUNIT_CASE(revocable_test_macro), + {} +}; + +static struct kunit_suite revocable_test_suite =3D { + .name =3D "revocable_test", + .test_cases =3D revocable_test_cases, +}; + +kunit_test_suite(revocable_test_suite); --=20 2.51.0.788.g6d19910ace-goog From nobody Wed Dec 17 14:36:47 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5849E23ABB9; Thu, 16 Oct 2025 05:43:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593382; cv=none; b=PxJqmXXqz5J9OgUJNxzLkjMvsrJcl46/kBewWrRqQaznRc3EGZs27wprAPWUNxYQl4P3WQbhQ1iAYrF2n508w3xjNmCTx42kelIbHMks9Jp7K2XTmV0l0wnbosOCNk78It6+M7LNqH44x9BuCxEl9cUoeT7WhRQKQA1U77a7TCs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593382; c=relaxed/simple; bh=Nj/qXhVShqIQkLxR4oWGDNi4Z/Jn2cjhmgEx0jHFZU0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iQFXg4DcRanQEFvsF4yacylijFOoMxFGLKTVZnLgrLq3XhIGrAsb2Vadw2aFB/h5XpCl11L9NNSdLAWyoYeFAunGA/2PeZbKSxWSvVqfVGVAVXZ7Mym4qL8PwyhxbJeRy5J0OVBrT/XCK7OHpTcz8VvpYF3MHP+GgeUIEj8K1Y8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pWOmFgv9; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="pWOmFgv9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5600AC4CEF1; Thu, 16 Oct 2025 05:42:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760593380; bh=Nj/qXhVShqIQkLxR4oWGDNi4Z/Jn2cjhmgEx0jHFZU0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pWOmFgv9EJZX21GRtghKojpDiHAuVyldd3pHAXVWhHGzphtJ0MJs+nEimlFNejpeJ OqkaiQuRoo6kJCf0mZDnzlGpAFEhyfWn42KEJO/6Ln0kdaNSp7702ZMKxYSsG5klrj qBZ03GcTCA2u9NGKV8SRReVkmKVJQyem4vmhXc+7uhyUUEq1y4ufQZEwREYOjFOuBz oa3ms/hCkp6/Cl9YbmmSN3iR99e8uFWTWQided0RLA17ubep/RBIKAnU5QBX+tETpP KhZ6gyFUAC4ggTHHu1QvBS3/oI3ALc/akKUnrr9XlkqXm8EMim4eItztWIOCVc5F2B 9VvRDy8HxAJug== From: Tzung-Bi Shih To: Benson Leung , Greg Kroah-Hartman , "Rafael J . Wysocki" , Danilo Krummrich Cc: Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, tzungbi@kernel.org, Laurent Pinchart , Bartosz Golaszewski , Wolfram Sang , Simona Vetter , Dan Williams , Jason Gunthorpe Subject: [PATCH v5 3/7] selftests: revocable: Add kselftest cases Date: Thu, 16 Oct 2025 05:42:00 +0000 Message-ID: <20251016054204.1523139-4-tzungbi@kernel.org> X-Mailer: git-send-email 2.51.0.788.g6d19910ace-goog In-Reply-To: <20251016054204.1523139-1-tzungbi@kernel.org> References: <20251016054204.1523139-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add kselftest cases for the revocable API. The test consists of three parts: - A kernel module (revocable_test.ko) that creates a debugfs interface with `/provider` and `/consumer` files. - A user-space C program (revocable_test) that uses the kselftest harness to interact with the debugfs files. - An orchestrating shell script (test-revocable.sh) that loads the module, runs the C program, and unloads the module. The test cases cover the following scenarios: - Basic: Verifies that a consumer can successfully access the resource provided via the provider. - Revocation: Verifies that after the provider revokes the resource, the consumer correctly receives a NULL pointer on a subsequent access. - Macro: Same as "Revocation" but uses the REVOCABLE(). Signed-off-by: Tzung-Bi Shih --- v5: - No changes. v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-4-tzungbi= @kernel.org - REVOCABLE() -> REVOCABLE_TRY_ACCESS_WITH(). - revocable_release() -> revocable_withdraw_access(). v3: https://lore.kernel.org/chrome-platform/20250912081718.3827390-4-tzungb= i@kernel.org - No changes. v2: https://lore.kernel.org/chrome-platform/20250820081645.847919-4-tzungbi= @kernel.org - New in the series. A way to run the kselftest (for my reference): - Update kernel to the DUT. - `mkdir build` and copy the kernel config to build/. - `make O=3Dbuild LLVM=3D1 -j32` (for generated headers and built-in symbol= s). - `make O=3Dbuild LLVM=3D1 KDIR=3D$(pwd) -C tools/testing/selftests/ TARGETS=3Ddrivers/base/revocable gen_tar`. - Copy build/kselftest/kselftest_install/kselftest-packages/kselftest.tar.gz to the DUT, extract, and execute the run_kselftest.sh. MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + .../selftests/drivers/base/revocable/Makefile | 7 + .../drivers/base/revocable/revocable_test.c | 116 +++++++++++ .../drivers/base/revocable/test-revocable.sh | 39 ++++ .../base/revocable/test_modules/Makefile | 10 + .../revocable/test_modules/revocable_test.c | 188 ++++++++++++++++++ 7 files changed, 362 insertions(+) create mode 100644 tools/testing/selftests/drivers/base/revocable/Makefile create mode 100644 tools/testing/selftests/drivers/base/revocable/revocabl= e_test.c create mode 100755 tools/testing/selftests/drivers/base/revocable/test-rev= ocable.sh create mode 100644 tools/testing/selftests/drivers/base/revocable/test_mod= ules/Makefile create mode 100644 tools/testing/selftests/drivers/base/revocable/test_mod= ules/revocable_test.c diff --git a/MAINTAINERS b/MAINTAINERS index 7207538f31f4..235a1d6e3718 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22027,6 +22027,7 @@ S: Maintained F: drivers/base/revocable.c F: drivers/base/revocable_test.c F: include/linux/revocable.h +F: tools/testing/selftests/drivers/base/revocable/ =20 RFKILL M: Johannes Berg diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Mak= efile index c46ebdb9b8ef..1136a8f12ef5 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -17,6 +17,7 @@ TARGETS +=3D damon TARGETS +=3D devices/error_logs TARGETS +=3D devices/probe TARGETS +=3D dmabuf-heaps +TARGETS +=3D drivers/base/revocable TARGETS +=3D drivers/dma-buf TARGETS +=3D drivers/ntsync TARGETS +=3D drivers/s390x/uvdevice diff --git a/tools/testing/selftests/drivers/base/revocable/Makefile b/tool= s/testing/selftests/drivers/base/revocable/Makefile new file mode 100644 index 000000000000..afa5ca0fa452 --- /dev/null +++ b/tools/testing/selftests/drivers/base/revocable/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +TEST_GEN_MODS_DIR :=3D test_modules +TEST_GEN_PROGS_EXTENDED :=3D revocable_test +TEST_PROGS :=3D test-revocable.sh + +include ../../../lib.mk diff --git a/tools/testing/selftests/drivers/base/revocable/revocable_test.= c b/tools/testing/selftests/drivers/base/revocable/revocable_test.c new file mode 100644 index 000000000000..3c0feb7a8944 --- /dev/null +++ b/tools/testing/selftests/drivers/base/revocable/revocable_test.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 Google LLC + * + * A selftest for the revocable API. + * + * The test cases cover the following scenarios: + * + * - Basic: Verifies that a consumer can successfully access the resource + * provided via the provider. + * + * - Revocation: Verifies that after the provider revokes the resource, + * the consumer correctly receives a NULL pointer on a subsequent access. + * + * - Macro: Same as "Revocation" but uses the REVOCABLE_TRY_ACCESS_WITH(). + */ + +#include +#include + +#include "../../../kselftest_harness.h" + +#define DEBUGFS_PATH "/sys/kernel/debug/revocable_test" +#define TEST_CMD_RESOURCE_GONE "resource_gone" +#define TEST_DATA "12345678" +#define TEST_MAGIC_OFFSET 0x1234 + +FIXTURE(revocable_fixture) { + int pfd; + int cfd; +}; + +FIXTURE_SETUP(revocable_fixture) { + int ret; + + self->pfd =3D open(DEBUGFS_PATH "/provider", O_WRONLY); + ASSERT_NE(-1, self->pfd) + TH_LOG("failed to open provider fd"); + + ret =3D write(self->pfd, TEST_DATA, strlen(TEST_DATA)); + ASSERT_NE(-1, ret) { + close(self->pfd); + TH_LOG("failed to write test data"); + } + + self->cfd =3D open(DEBUGFS_PATH "/consumer", O_RDONLY); + ASSERT_NE(-1, self->cfd) + TH_LOG("failed to open consumer fd"); +} + +FIXTURE_TEARDOWN(revocable_fixture) { + close(self->cfd); + close(self->pfd); +} + +/* + * ASSERT_* is only available in TEST or TEST_F block. Use + * macro for the helper. + */ +#define READ_TEST_DATA(_fd, _offset, _data, _msg) \ + do { \ + int ret; \ + \ + ret =3D lseek(_fd, _offset, SEEK_SET); \ + ASSERT_NE(-1, ret) \ + TH_LOG("failed to lseek"); \ + \ + ret =3D read(_fd, _data, sizeof(_data) - 1); \ + ASSERT_NE(-1, ret) \ + TH_LOG(_msg); \ + data[ret] =3D '\0'; \ + } while (0) + +TEST_F(revocable_fixture, basic) { + char data[16]; + + READ_TEST_DATA(self->cfd, 0, data, "failed to read test data"); + EXPECT_STREQ(TEST_DATA, data); +} + +TEST_F(revocable_fixture, revocation) { + char data[16]; + int ret; + + READ_TEST_DATA(self->cfd, 0, data, "failed to read test data"); + EXPECT_STREQ(TEST_DATA, data); + + ret =3D write(self->pfd, TEST_CMD_RESOURCE_GONE, + strlen(TEST_CMD_RESOURCE_GONE)); + ASSERT_NE(-1, ret) + TH_LOG("failed to write resource gone cmd"); + + READ_TEST_DATA(self->cfd, 0, data, + "failed to read test data after resource gone"); + EXPECT_STREQ("(null)", data); +} + +TEST_F(revocable_fixture, macro) { + char data[16]; + int ret; + + READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET, data, + "failed to read test data"); + EXPECT_STREQ(TEST_DATA, data); + + ret =3D write(self->pfd, TEST_CMD_RESOURCE_GONE, + strlen(TEST_CMD_RESOURCE_GONE)); + ASSERT_NE(-1, ret) + TH_LOG("failed to write resource gone cmd"); + + READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET, data, + "failed to read test data after resource gone"); + EXPECT_STREQ("(null)", data); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/drivers/base/revocable/test-revocable.= sh b/tools/testing/selftests/drivers/base/revocable/test-revocable.sh new file mode 100755 index 000000000000..3a34be28001a --- /dev/null +++ b/tools/testing/selftests/drivers/base/revocable/test-revocable.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +mod_name=3D"revocable_test" +ksft_fail=3D1 +ksft_skip=3D4 + +if [ "$(id -u)" -ne 0 ]; then + echo "$0: Must be run as root" + exit "$ksft_skip" +fi + +if ! which insmod > /dev/null 2>&1; then + echo "$0: Need insmod" + exit "$ksft_skip" +fi + +if ! which rmmod > /dev/null 2>&1; then + echo "$0: Need rmmod" + exit "$ksft_skip" +fi + +insmod test_modules/"${mod_name}".ko + +if [ ! -d /sys/kernel/debug/revocable_test/ ]; then + mount -t debugfs none /sys/kernel/debug/ + + if [ ! -d /sys/kernel/debug/revocable_test/ ]; then + echo "$0: Error mounting debugfs" + exit "$ksft_fail" + fi +fi + +./revocable_test +ret=3D$? + +rmmod "${mod_name}" + +exit "${ret}" diff --git a/tools/testing/selftests/drivers/base/revocable/test_modules/Ma= kefile b/tools/testing/selftests/drivers/base/revocable/test_modules/Makefi= le new file mode 100644 index 000000000000..f29e4f909402 --- /dev/null +++ b/tools/testing/selftests/drivers/base/revocable/test_modules/Makefile @@ -0,0 +1,10 @@ +TESTMODS_DIR :=3D $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))= )) +KDIR ?=3D /lib/modules/$(shell uname -r)/build + +obj-m +=3D revocable_test.o + +all: + $(Q)$(MAKE) -C $(KDIR) M=3D$(TESTMODS_DIR) + +clean: + $(Q)$(MAKE) -C $(KDIR) M=3D$(TESTMODS_DIR) clean diff --git a/tools/testing/selftests/drivers/base/revocable/test_modules/re= vocable_test.c b/tools/testing/selftests/drivers/base/revocable/test_module= s/revocable_test.c new file mode 100644 index 000000000000..c90ec150a6c8 --- /dev/null +++ b/tools/testing/selftests/drivers/base/revocable/test_modules/revocable= _test.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 Google LLC + * + * A kernel module for testing the revocable API. + */ + +#include +#include +#include +#include + +#define TEST_CMD_RESOURCE_GONE "resource_gone" +#define TEST_MAGIC_OFFSET 0x1234 + +static struct dentry *debugfs_dir; + +struct revocable_test_provider_priv { + struct revocable_provider *rp; + struct dentry *dentry; + char res[16]; +}; + +static int revocable_test_consumer_open(struct inode *inode, struct file *= filp) +{ + struct revocable *rev; + struct revocable_provider *rp =3D inode->i_private; + + rev =3D revocable_alloc(rp); + if (!rev) + return -ENOMEM; + filp->private_data =3D rev; + + return 0; +} + +static int revocable_test_consumer_release(struct inode *inode, + struct file *filp) +{ + struct revocable *rev =3D filp->private_data; + + revocable_free(rev); + return 0; +} + +static ssize_t revocable_test_consumer_read(struct file *filp, + char __user *buf, + size_t count, loff_t *offset) +{ + char *res; + char data[16]; + size_t len; + struct revocable *rev =3D filp->private_data; + + switch (*offset) { + case 0: + res =3D revocable_try_access(rev); + snprintf(data, sizeof(data), "%s", res ?: "(null)"); + revocable_withdraw_access(rev); + break; + case TEST_MAGIC_OFFSET: + REVOCABLE_TRY_ACCESS_WITH(rev, res) + snprintf(data, sizeof(data), "%s", res ?: "(null)"); + break; + default: + return 0; + } + + len =3D min_t(size_t, strlen(data), count); + if (copy_to_user(buf, data, len)) + return -EFAULT; + + *offset =3D len; + return len; +} + +static const struct file_operations revocable_test_consumer_fops =3D { + .open =3D revocable_test_consumer_open, + .release =3D revocable_test_consumer_release, + .read =3D revocable_test_consumer_read, + .llseek =3D default_llseek, +}; + +static int revocable_test_provider_open(struct inode *inode, struct file *= filp) +{ + struct revocable_test_provider_priv *priv; + + priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + filp->private_data =3D priv; + + return 0; +} + +static int revocable_test_provider_release(struct inode *inode, + struct file *filp) +{ + struct revocable_test_provider_priv *priv =3D filp->private_data; + + debugfs_remove(priv->dentry); + if (priv->rp) + revocable_provider_revoke(priv->rp); + kfree(priv); + + return 0; +} + +static ssize_t revocable_test_provider_write(struct file *filp, + const char __user *buf, + size_t count, loff_t *offset) +{ + size_t copied; + char data[64]; + struct revocable_test_provider_priv *priv =3D filp->private_data; + + copied =3D strncpy_from_user(data, buf, sizeof(data)); + if (copied < 0) + return copied; + if (copied =3D=3D sizeof(data)) + data[sizeof(data) - 1] =3D '\0'; + + /* + * Note: The test can't just close the FD for signaling the + * resource gone. Subsequent file operations on the opening + * FD of debugfs return -EIO after calling debugfs_remove(). + * See also debugfs_file_get(). + * + * Here is a side command channel for signaling the resource + * gone. + */ + if (!strcmp(data, TEST_CMD_RESOURCE_GONE)) { + revocable_provider_revoke(priv->rp); + priv->rp =3D NULL; + } else { + if (priv->res[0] !=3D '\0') + return 0; + + strscpy(priv->res, data); + + priv->rp =3D revocable_provider_alloc(&priv->res); + if (!priv->rp) + return -ENOMEM; + + priv->dentry =3D debugfs_create_file("consumer", 0400, + debugfs_dir, priv->rp, + &revocable_test_consumer_fops); + if (!priv->dentry) { + revocable_provider_revoke(priv->rp); + return -ENOMEM; + } + } + + return copied; +} + +static const struct file_operations revocable_test_provider_fops =3D { + .open =3D revocable_test_provider_open, + .release =3D revocable_test_provider_release, + .write =3D revocable_test_provider_write, +}; + +static int __init revocable_test_init(void) +{ + debugfs_dir =3D debugfs_create_dir("revocable_test", NULL); + if (!debugfs_dir) + return -ENOMEM; + + if (!debugfs_create_file("provider", 0200, debugfs_dir, NULL, + &revocable_test_provider_fops)) { + debugfs_remove_recursive(debugfs_dir); + return -ENOMEM; + } + + return 0; +} + +static void __exit revocable_test_exit(void) +{ + debugfs_remove_recursive(debugfs_dir); +} + +module_init(revocable_test_init); +module_exit(revocable_test_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tzung-Bi Shih "); +MODULE_DESCRIPTION("Revocable Kselftest"); --=20 2.51.0.788.g6d19910ace-goog From nobody Wed Dec 17 14:36:47 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE2152459ED; Thu, 16 Oct 2025 05:43:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593384; cv=none; b=M6C4sK+lmpvYNKDIL2ed2qI3EsIPzbKb+maMwNLRGk9W5xv4DfdtkIVl8OrjtVdo6gEdzoq4aoqG+szE5sdtsPXamBycArHc6DDu31s+wTi3VoxtSmKhS9IVxT7TRh9hGgLMTPwGH0UHXCnkdhpHPEfrQCCg6qFywRIJi1iB8S0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593384; c=relaxed/simple; bh=XRROXfFgCgBPt8A6SqNymPJ90XRaZ5e3kZHr9tE4vXs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YGJruG7l6ga0MyaX1hAeyPxSh9VSpOBFZb9DP4xwmCmIM0GJGhlqwqlul8ThHkHalHoEKEWqu49TePyjeRsEYjPbhUXl6/FjH/lu6s9aSffmtSQwKRKSET46dchxs9JB25/lAPMy4+mpxFqT0qJ9m68p0+XZUoumkYgdQVtU2M0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=o2LoO9XD; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="o2LoO9XD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 30171C4CEFE; Thu, 16 Oct 2025 05:43:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760593383; bh=XRROXfFgCgBPt8A6SqNymPJ90XRaZ5e3kZHr9tE4vXs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o2LoO9XDGvNzglIKrvnX4JhNVvJtVwYpl4Q4GVfQPZ9TO08vCbUsPx/OOejz33+vc aKp95sXhQwwlYGG1peoIUTM3QCw9vyJgImBYX8vgRy0UBk0V/VszLS71Jb4yGpKb+d NOBRzfCe1D8bVSOvBlr2yEl0IdLusBSHS94Hp7HuAus4EtdUKK1z4sRdJ+0fH/4rUx 0RBnx7m3S56suXBI6PBRztvMphHqnI5N5B0Y4uV3aPTD2sslTPRvfkPJ//7r9QWlPf x6dKZj8ezQUYDx9APHuyhXZBjyTguROF/WUPqKJD/RqCCkKqvxv80O6dX4AQaRiNn+ D57tpE/1vuNeg== From: Tzung-Bi Shih To: Benson Leung , Greg Kroah-Hartman , "Rafael J . Wysocki" , Danilo Krummrich Cc: Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, tzungbi@kernel.org, Laurent Pinchart , Bartosz Golaszewski , Wolfram Sang , Simona Vetter , Dan Williams , Jason Gunthorpe Subject: [PATCH v5 4/7] platform/chrome: Protect cros_ec_device lifecycle with revocable Date: Thu, 16 Oct 2025 05:42:01 +0000 Message-ID: <20251016054204.1523139-5-tzungbi@kernel.org> X-Mailer: git-send-email 2.51.0.788.g6d19910ace-goog In-Reply-To: <20251016054204.1523139-1-tzungbi@kernel.org> References: <20251016054204.1523139-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The cros_ec_device can be unregistered when the underlying device is removed. Other kernel drivers that interact with the EC may hold a pointer to the cros_ec_device, creating a risk of a use-after-free error if the EC device is removed while still being referenced. To prevent this, leverage the revocable and convert the underlying device drivers to resource providers of cros_ec_device. Signed-off-by: Tzung-Bi Shih --- v5: - No changes. v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-5-tzungbi= @kernel.org - No changes. v3: https://lore.kernel.org/chrome-platform/20250912081718.3827390-5-tzungb= i@kernel.org - Initialize the revocable provider in cros_ec_device_alloc() instead of spreading in protocol device drivers. v2: https://lore.kernel.org/chrome-platform/20250820081645.847919-5-tzungbi= @kernel.org - Rename "ref_proxy" -> "revocable". v1: https://lore.kernel.org/chrome-platform/20250814091020.1302888-3-tzungb= i@kernel.org drivers/platform/chrome/cros_ec.c | 5 +++++ include/linux/platform_data/cros_ec_proto.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cr= os_ec.c index 1da79e3d215b..95e3e898e3da 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include =20 @@ -47,6 +48,10 @@ struct cros_ec_device *cros_ec_device_alloc(struct devic= e *dev) if (!ec_dev) return NULL; =20 + ec_dev->revocable_provider =3D devm_revocable_provider_alloc(dev, ec_dev); + if (!ec_dev->revocable_provider) + return NULL; + ec_dev->din_size =3D sizeof(struct ec_host_response) + sizeof(struct ec_response_get_protocol_info) + EC_MAX_RESPONSE_OVERHEAD; diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/pl= atform_data/cros_ec_proto.h index de14923720a5..fbb6ca34a40f 100644 --- a/include/linux/platform_data/cros_ec_proto.h +++ b/include/linux/platform_data/cros_ec_proto.h @@ -12,6 +12,7 @@ #include #include #include +#include =20 #include =20 @@ -165,6 +166,7 @@ struct cros_ec_command { * @pd: The platform_device used by the mfd driver to interface with the * PD behind an EC. * @panic_notifier: EC panic notifier. + * @revocable_provider: The revocable_provider to this device. */ struct cros_ec_device { /* These are used by other drivers that want to talk to the EC */ @@ -211,6 +213,8 @@ struct cros_ec_device { struct platform_device *pd; =20 struct blocking_notifier_head panic_notifier; + + struct revocable_provider *revocable_provider; }; =20 /** --=20 2.51.0.788.g6d19910ace-goog From nobody Wed Dec 17 14:36:47 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1E8A92459ED; Thu, 16 Oct 2025 05:43:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593387; cv=none; b=WAYJ3C7kdwgalCNVRczLcqjyJ3rve9dFKn8KjegFi8oAA9VZpuuIJrf/8+5E+yXMph6uYHuCRItDz7ozwT97PujGA76FtTQ9MbryRR3Bbb4DnDyurjeMsaVswBrvKRPo5UGcp4oIZqxow+r5gZJRWSyvJty2a5TUs0YNdHnCAlE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593387; c=relaxed/simple; bh=8BMbHiSLkDMMANbw+KKMDG0ygUk8JdmWnPcT/VE9x8w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Sqib8iwY4yLrcZVD1HsGjd9KXPPWmnAI7y0/L7APZscqgWb59FzMI45JWRiAVgr5+yatmGEZZaByZUarRZLSOo6n6Ue32RorltLtGLgxXDP1+MX1d+h8ebg+my4qDr0Xwsn0/L1fE/unIX/3mtqd314P5CR48o8o97uygD+k3lQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=K8O9WK13; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="K8O9WK13" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1714FC4CEF1; Thu, 16 Oct 2025 05:43:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760593386; bh=8BMbHiSLkDMMANbw+KKMDG0ygUk8JdmWnPcT/VE9x8w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=K8O9WK13a9InIrG8b5bi7SmmDmjkzzGQhaZPwU7sHBzskRbscLVSYU8ailonuYpYg L4TvTLFHtU5ZNCYKTt4FVDx7D8W416KzFd68CWxJ5wFMqUh4cGH7zv3x4BdO6wH18l BmaoMvWKL10eJGH8SMldSp1kD561x2l7yidQ4ZlnVlScouTq0XUpkjFogSC+zNgwR9 Ui3NAO19SxnWMFj3E0KyfFks5PDJKyPvinI9bkkPIQmwN6zJMYt/nkF6G1ztbSFvAj B8YH37XsuDhS/CxmrQSPZ9UresUZYLKzUfNhfZC52oELiyNPUBVOcZjOSxqxWY5wZt PxGpLxrrtm8UQ== From: Tzung-Bi Shih To: Benson Leung , Greg Kroah-Hartman , "Rafael J . Wysocki" , Danilo Krummrich Cc: Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, tzungbi@kernel.org, Laurent Pinchart , Bartosz Golaszewski , Wolfram Sang , Simona Vetter , Dan Williams , Jason Gunthorpe Subject: [PATCH v5 5/7] revocable: Add fops replacement Date: Thu, 16 Oct 2025 05:42:02 +0000 Message-ID: <20251016054204.1523139-6-tzungbi@kernel.org> X-Mailer: git-send-email 2.51.0.788.g6d19910ace-goog In-Reply-To: <20251016054204.1523139-1-tzungbi@kernel.org> References: <20251016054204.1523139-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce fs_revocable_replace() to simplify the use of the revocable API with file_operations. The function, should be called from a driver's ->open(), replaces the fops with a wrapper that automatically handles the `try_access` and `withdraw_access`. When the file is closed, the wrapper's ->release() restores the original fops and cleanups. This centralizes the revocable logic, making drivers cleaner and easier to maintain. Signed-off-by: Tzung-Bi Shih --- PoC patch. Known issues: - All file operations call revocable_try_access() for guaranteeing the resource even if the resource may be unused in the fops. v5: - Rename to "fs_revocable". - Move the replacement context to struct file. - Support multiple revocable providers. v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-6-tzungbi= @kernel.org - New in the series. fs/Makefile | 2 +- fs/fs_revocable.c | 154 +++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 + include/linux/fs_revocable.h | 21 +++++ 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 fs/fs_revocable.c create mode 100644 include/linux/fs_revocable.h diff --git a/fs/Makefile b/fs/Makefile index e3523ab2e587..ec2b21385deb 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -16,7 +16,7 @@ obj-y :=3D open.o read_write.o file_table.o super.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ fs_types.o fs_context.o fs_parser.o fsopen.o init.o \ kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \ - file_attr.o + file_attr.o fs_revocable.o =20 obj-$(CONFIG_BUFFER_HEAD) +=3D buffer.o mpage.o obj-$(CONFIG_PROC_FS) +=3D proc_namespace.o diff --git a/fs/fs_revocable.c b/fs/fs_revocable.c new file mode 100644 index 000000000000..4b6d755abfed --- /dev/null +++ b/fs/fs_revocable.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 Google LLC + * + * File operation replacement with Revocable + */ + +#include +#include +#include +#include + +struct fs_revocable_replacement { + const struct fs_revocable_operations *frops; + const struct file_operations *orig_fops; + struct file_operations fops; + struct revocable **revs; + size_t num_revs; +}; + +static int fs_revocable_try_access(struct file *filp) +{ + struct fs_revocable_replacement *rr =3D filp->f_rr; + + return rr->frops->try_access(rr->revs, rr->num_revs, + filp->private_data); +} + +static void fs_revocable_withdraw_access(struct fs_revocable_replacement *= rr) +{ + for (size_t i =3D 0; i < rr->num_revs; ++i) + revocable_withdraw_access(rr->revs[i]); +} + +DEFINE_FREE(fs_revocable_replacement, struct fs_revocable_replacement *, + if (_T) fs_revocable_withdraw_access(_T)) + +static ssize_t fs_revocable_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) +{ + struct fs_revocable_replacement *rr + __free(fs_revocable_replacement) =3D filp->f_rr; + int ret; + + ret =3D fs_revocable_try_access(filp); + if (ret) + return ret; + return rr->orig_fops->read(filp, buffer, length, offset); +} + +static __poll_t fs_revocable_poll(struct file *filp, poll_table *wait) +{ + struct fs_revocable_replacement *rr + __free(fs_revocable_replacement) =3D filp->f_rr; + int ret; + + ret =3D fs_revocable_try_access(filp); + if (ret) + return ret; + return rr->orig_fops->poll(filp, wait); +} + +static long fs_revocable_unlocked_ioctl(struct file *filp, unsigned int cm= d, + unsigned long arg) +{ + struct fs_revocable_replacement *rr + __free(fs_revocable_replacement) =3D filp->f_rr; + int ret; + + ret =3D fs_revocable_try_access(filp); + if (ret) + return ret; + return rr->orig_fops->unlocked_ioctl(filp, cmd, arg); +} + +static int fs_revocable_release(struct inode *inode, struct file *filp) +{ + int ret =3D 0; + struct fs_revocable_replacement *rr =3D filp->f_rr; + + if (!rr->orig_fops->release) + goto recover; + + ret =3D fs_revocable_try_access(filp); + if (ret) + goto recover; + + ret =3D rr->orig_fops->release(inode, filp); + + fs_revocable_withdraw_access(rr); +recover: + filp->f_op =3D rr->orig_fops; + filp->f_rr =3D NULL; + + for (size_t i =3D 0; i < rr->num_revs; ++i) + revocable_free(rr->revs[i]); + kfree(rr->revs); + kfree(rr); + + return ret; +} + +/** + * fs_revocable_replace() - Replace the file operations to be revocable-aw= are. + * + * Should be used only from ->open() instances. + */ +int fs_revocable_replace(struct file *filp, + const struct fs_revocable_operations *frops, + struct revocable_provider **rps, size_t num_rps) +{ + struct fs_revocable_replacement *rr; + size_t i; + + rr =3D kzalloc(sizeof(*rr), GFP_KERNEL); + if (!rr) + return -ENOMEM; + filp->f_rr =3D rr; + + rr->frops =3D frops; + rr->revs =3D kcalloc(num_rps, sizeof(*rr->revs), GFP_KERNEL); + if (!rr->revs) + goto free_rr; + for (i =3D 0; i < num_rps; ++i) { + rr->revs[i] =3D revocable_alloc(rps[i]); + if (!rr->revs[i]) + goto free_revs; + } + rr->num_revs =3D num_rps; + rr->orig_fops =3D filp->f_op; + + memcpy(&rr->fops, filp->f_op, sizeof(rr->fops)); + rr->fops.release =3D fs_revocable_release; + + if (rr->fops.read) + rr->fops.read =3D fs_revocable_read; + if (rr->fops.poll) + rr->fops.poll =3D fs_revocable_poll; + if (rr->fops.unlocked_ioctl) + rr->fops.unlocked_ioctl =3D fs_revocable_unlocked_ioctl; + + filp->f_op =3D &rr->fops; + return 0; +free_revs: + if (i) { + while (--i) + revocable_free(rr->revs[i]); + } + kfree(rr->revs); +free_rr: + kfree(rr); + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(fs_revocable_replace); diff --git a/include/linux/fs.h b/include/linux/fs.h index 68c4a59ec8fb..a03dd7f343a9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -80,6 +80,7 @@ struct fs_context; struct fs_parameter_spec; struct file_kattr; struct iomap_ops; +struct fs_revocable_replacement; =20 extern void __init inode_init(void); extern void __init inode_init_early(void); @@ -1248,6 +1249,7 @@ struct file { }; file_ref_t f_ref; /* --- cacheline 3 boundary (192 bytes) --- */ + struct fs_revocable_replacement *f_rr; } __randomize_layout __attribute__((aligned(4))); /* lest something weird decides that 2 is O= K */ =20 diff --git a/include/linux/fs_revocable.h b/include/linux/fs_revocable.h new file mode 100644 index 000000000000..bb066392d232 --- /dev/null +++ b/include/linux/fs_revocable.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2025 Google LLC + */ + +#ifndef __LINUX_FS_REVOCABLE_H +#define __LINUX_FS_REVOCABLE_H + +#include +#include + +struct fs_revocable_operations { + int (*try_access)(struct revocable **revs, size_t num_revs, void *data); +}; + +int fs_revocable_replace(struct file *filp, + const struct fs_revocable_operations *frops, + struct revocable_provider **rps, size_t num_rps); + +#endif /* __LINUX_FS_REVOCABLE_H */ --=20 2.51.0.788.g6d19910ace-goog From nobody Wed Dec 17 14:36:47 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1A07251793; Thu, 16 Oct 2025 05:43:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593389; cv=none; b=lRbDxzJl3QbHewPa+HsSZqP8l6+st/cG1FyRKfzAWYoNW3AhplyponU0i6Y6zAivkxQ2AB2HoXLq4wZg0CHDFpAW8OixTOgkxfiWTQlQDXFQk6cmjYf4EM54cvnykEk54w1jgVWI6kYi86CRx0xIL2pX3WC8q2jJ44Ia9Mwwk0o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593389; c=relaxed/simple; bh=I4XuDN4yktmrYsSnv6UDwJT4c61SEYgQLvhKVNQf2O4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZccHoD5ebQ0h/X+hxOZJVkBzWLGY3Zf325gSW+dci37wtJhpqVUqTN98Fr+Al8c1RFWCrutdKa6+bo5CTjbx7t+qQOr6zLuGqJcR+JcWG941QL6eBTLbnLcLagY04WimFVTD40kUDUO3M+CpjEeWbPZx5ZoEOuBPeBBcmqGUSnc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IA3xHqBu; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IA3xHqBu" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E3D72C4CEFB; Thu, 16 Oct 2025 05:43:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760593389; bh=I4XuDN4yktmrYsSnv6UDwJT4c61SEYgQLvhKVNQf2O4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IA3xHqBuTahiy7FZEE8jkseB6+LwzKHIXQWc3Z169sioMUb98utynTzoCiNf7ecCZ Xoz2xTjYjR10JYSrHQXhl3o2M2C5ne4IKCdAf9hcDEzzoebC02pXXDJ56GOdulBT4f Z6E82sQFH1RxDxh9i/5BLJVg/3FFwyrLcVXNtmL5vPy7JFOiIRr1OUtMvJ5nYiHdUx 4Xsz25U5+m7aph+Oim7EU7bfPKmn7Nm7mgV8Jskuu+NlN6od1AY+L89TG8QSFG1hgq Zq9OHJNJTjsUA6W75Az2nJ+nZymqBD/tYSneGdByMfJAgj+4Assc+u+R+W/ck+3lw9 g1ga55QojYmYQ== From: Tzung-Bi Shih To: Benson Leung , Greg Kroah-Hartman , "Rafael J . Wysocki" , Danilo Krummrich Cc: Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, tzungbi@kernel.org, Laurent Pinchart , Bartosz Golaszewski , Wolfram Sang , Simona Vetter , Dan Williams , Jason Gunthorpe Subject: [PATCH v5 6/7] char: misc: Leverage revocable fops replacement Date: Thu, 16 Oct 2025 05:42:03 +0000 Message-ID: <20251016054204.1523139-7-tzungbi@kernel.org> X-Mailer: git-send-email 2.51.0.788.g6d19910ace-goog In-Reply-To: <20251016054204.1523139-1-tzungbi@kernel.org> References: <20251016054204.1523139-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Signed-off-by: Tzung-Bi Shih --- PoC patch. v5: - No primary changes but modify the API usage accordingly to support multiple revocable providers. v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-7-tzungbi= @kernel.org - New in the series. drivers/char/misc.c | 8 ++++++++ include/linux/miscdevice.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 726516fb0a3b..5b412e18b8a6 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -50,6 +50,7 @@ #include #include #include +#include =20 /* * Head entry for the doubly linked miscdevice list @@ -159,6 +160,13 @@ static int misc_open(struct inode *inode, struct file = *file) =20 err =3D 0; replace_fops(file, new_fops); + + if (c->frops && c->rps) { + err =3D fs_revocable_replace(file, c->frops, c->rps, c->num_rps); + if (err) + goto fail; + } + if (file->f_op->open) err =3D file->f_op->open(inode, file); fail: diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 7d0aa718499c..b7024a0cebb9 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -5,6 +5,7 @@ #include #include #include +#include =20 /* * These allocations are managed by device@lanana.org. If you need @@ -92,6 +93,9 @@ struct miscdevice { const struct attribute_group **groups; const char *nodename; umode_t mode; + struct revocable_provider **rps; + size_t num_rps; + const struct fs_revocable_operations *frops; }; =20 extern int misc_register(struct miscdevice *misc); --=20 2.51.0.788.g6d19910ace-goog From nobody Wed Dec 17 14:36:47 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4C242550CA; Thu, 16 Oct 2025 05:43:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593392; cv=none; b=KujLf1Jj+OOCf94I7HJ40l1R6748NFrCg+uwuQhd3CG2nR6n0bMTtl7pLhWUFd975AG0yF/FWUQZ9mBqgPF2VYxPzA1gvRbWQuIZgZvHqudBgofIgL/GrYEp0Rj08bPLjAyvIElSX2BsHdrLQjPJWDpmgVRLUe+x4tJ4nNRXBkM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760593392; c=relaxed/simple; bh=eIVI8OqgEV3xHp+az9XF8OTJlngtcvHJX8WxH8mJ3xQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QMHVZ6xwUqT29jYX/iZeSnMmsWFV/fUnWxajYe8w8RTu2dUHDC2N5bmNsBz9ZkrImKHbfj88yj/0VghrC0/z+jo00Tv9FYhJ6h2xnbSJF7K389ltI/tqxivW45nbMc6zwwv9n2TJBKsBZ4isAxmO4sM0EOKNzp8D1s0LUqrQsl4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cjeExjP4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cjeExjP4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BC44AC4CEFE; Thu, 16 Oct 2025 05:43:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760593392; bh=eIVI8OqgEV3xHp+az9XF8OTJlngtcvHJX8WxH8mJ3xQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cjeExjP46ZUNLmcY7FIHFH8hwwe5PyprJG4sl+TBQKzk+A1GardBEaKZaNA8alXeM Gu967SAsggH42UyYYtUCq1A4L/at5QaeM6drFxlpbMy9Q3uF7PEjcAwjVipIPrpxMu ntENjMeNC1yZlLMQIVk2YYYFWMWzRd/S11/5J9FNVhwG19aojHMZ0TBmqkW+70fnO6 pgopi7c4Wb57uRIkw0Kw9zPhSpO1e/gIyMmGGO8CpUnjvEt3H+UR/kc6ntIr55ngYs 0HYZAZhnH2frOoL4+/uhdPMPjGaVFIAKjGVzBzc19mMtCggQ0zhszm5DP8KH+XBa72 8f2ctTv2EGbzA== From: Tzung-Bi Shih To: Benson Leung , Greg Kroah-Hartman , "Rafael J . Wysocki" , Danilo Krummrich Cc: Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, tzungbi@kernel.org, Laurent Pinchart , Bartosz Golaszewski , Wolfram Sang , Simona Vetter , Dan Williams , Jason Gunthorpe Subject: [PATCH v5 7/7] platform/chrome: cros_ec_chardev: Secure cros_ec_device via revocable Date: Thu, 16 Oct 2025 05:42:04 +0000 Message-ID: <20251016054204.1523139-8-tzungbi@kernel.org> X-Mailer: git-send-email 2.51.0.788.g6d19910ace-goog In-Reply-To: <20251016054204.1523139-1-tzungbi@kernel.org> References: <20251016054204.1523139-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Miscdevice now supports revocable fops replacement. Use it to secure the cros_ec_device. Signed-off-by: Tzung-Bi Shih --- PoC patch. v5: - No primary changes but modify the API usage accordingly. v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-8-tzungbi= @kernel.org - New in the series. drivers/platform/chrome/cros_ec_chardev.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_chardev.c b/drivers/platform/c= hrome/cros_ec_chardev.c index c9d80ad5b57e..01691b023d7e 100644 --- a/drivers/platform/chrome/cros_ec_chardev.c +++ b/drivers/platform/chrome/cros_ec_chardev.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -166,7 +167,6 @@ static int cros_ec_chardev_open(struct inode *inode, st= ruct file *filp) if (!priv) return -ENOMEM; =20 - priv->ec_dev =3D ec_dev; priv->cmd_offset =3D ec->cmd_offset; filp->private_data =3D priv; INIT_LIST_HEAD(&priv->events); @@ -370,6 +370,19 @@ static const struct file_operations chardev_fops =3D { #endif }; =20 +static int cros_ec_chardev_rev_try_access(struct revocable **revs, + size_t num_revs, void *data) +{ + struct chardev_priv *priv =3D data; + + priv->ec_dev =3D revocable_try_access(revs[0]); + return priv->ec_dev ? 0 : -ENODEV; +} + +static const struct fs_revocable_operations cros_ec_chardev_frops =3D { + .try_access =3D cros_ec_chardev_rev_try_access, +}; + static int cros_ec_chardev_probe(struct platform_device *pdev) { struct cros_ec_dev *ec =3D dev_get_drvdata(pdev->dev.parent); @@ -386,6 +399,13 @@ static int cros_ec_chardev_probe(struct platform_devic= e *pdev) misc->name =3D ec_platform->ec_name; misc->parent =3D pdev->dev.parent; =20 + misc->rps =3D devm_kcalloc(&pdev->dev, 1, sizeof(*misc->rps), GFP_KERNEL); + if (!misc->rps) + return -ENOMEM; + misc->rps[0] =3D ec->ec_dev->revocable_provider; + misc->num_rps =3D 1; + misc->frops =3D &cros_ec_chardev_frops; + dev_set_drvdata(&pdev->dev, misc); =20 return misc_register(misc); --=20 2.51.0.788.g6d19910ace-goog