From: Tzung-Bi Shih <tzungbi@kernel.org>
To: Arnd Bergmann <arnd@arndb.de>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Bartosz Golaszewski <brgl@kernel.org>,
Linus Walleij <linusw@kernel.org>
Cc: Benson Leung <bleung@chromium.org>,
tzungbi@kernel.org, linux-kernel@vger.kernel.org,
chrome-platform@lists.linux.dev, driver-core@lists.linux.dev,
linux-doc@vger.kernel.org, linux-gpio@vger.kernel.org,
"Rafael J. Wysocki" <rafael@kernel.org>,
Danilo Krummrich <dakr@kernel.org>,
Jonathan Corbet <corbet@lwn.net>, Shuah Khan <shuah@kernel.org>,
Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
Wolfram Sang <wsa+renesas@sang-engineering.com>,
Jason Gunthorpe <jgg@nvidia.com>, Johan Hovold <johan@kernel.org>,
"Paul E . McKenney" <paulmck@kernel.org>
Subject: [PATCH v11 5/5] platform/chrome: cros_ec_chardev: Consume cros_ec_device via revocable
Date: Wed, 13 May 2026 17:10:43 +0800 [thread overview]
Message-ID: <20260513091043.6766-6-tzungbi@kernel.org> (raw)
In-Reply-To: <20260513091043.6766-1-tzungbi@kernel.org>
The cros_ec_chardev driver provides a character device interface to the
ChromeOS EC. A file handle to this device can remain open in userspace
even if the underlying EC device is removed.
This creates a classic use-after-free vulnerability. Any file operation
(ioctl, release, etc.) on the open handle after the EC device has gone
would access a stale pointer, leading to a system crash.
To prevent this, leverage the revocable and convert cros_ec_chardev to a
resource consumer of cros_ec_device.
---
v11:
- No changes.
v10: https://lore.kernel.org/all/20260508105448.31799-10-tzungbi@kernel.org
- No changes.
v9: https://lore.kernel.org/all/20260427135841.96266-10-tzungbi@kernel.org
- New to the series.
- Change revocable API usages accordingly.
v4 - v8:
- Doesn't exist.
v3: https://lore.kernel.org/all/20250912081718.3827390-6-tzungbi@kernel.org
- Use specific labels for different cleanup in cros_ec_chardev_open().
v2: https://lore.kernel.org/all/20250820081645.847919-6-tzungbi@kernel.org
- Rename "ref_proxy" -> "revocable".
- Fix a sparse warning by removing the redundant __rcu annotation.
v1: https://lore.kernel.org/all/20250814091020.1302888-4-tzungbi@kernel.org
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
drivers/platform/chrome/cros_ec_chardev.c | 80 +++++++++++++++++------
1 file changed, 61 insertions(+), 19 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_chardev.c b/drivers/platform/chrome/cros_ec_chardev.c
index 002be3352100..c597dc92d519 100644
--- a/drivers/platform/chrome/cros_ec_chardev.c
+++ b/drivers/platform/chrome/cros_ec_chardev.c
@@ -22,6 +22,7 @@
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
+#include <linux/revocable.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -32,7 +33,7 @@
#define CROS_MAX_EVENT_LEN PAGE_SIZE
struct chardev_priv {
- struct cros_ec_device *ec_dev;
+ struct revocable *rev;
struct notifier_block notifier;
wait_queue_head_t wait_event;
unsigned long event_mask;
@@ -55,6 +56,7 @@ static int ec_get_version(struct chardev_priv *priv, char *str, int maxlen)
};
struct ec_response_get_version *resp;
struct cros_ec_command *msg;
+ struct cros_ec_device *ec_dev;
int ret;
msg = kzalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
@@ -64,12 +66,19 @@ static int ec_get_version(struct chardev_priv *priv, char *str, int maxlen)
msg->command = EC_CMD_GET_VERSION + priv->cmd_offset;
msg->insize = sizeof(*resp);
- ret = cros_ec_cmd_xfer_status(priv->ec_dev, msg);
- if (ret < 0) {
- snprintf(str, maxlen,
- "Unknown EC version, returned error: %d\n",
- msg->result);
- goto exit;
+ revocable_try_access_with_scoped(priv->rev, ec_dev) {
+ if (!ec_dev) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ ret = cros_ec_cmd_xfer_status(ec_dev, msg);
+ if (ret < 0) {
+ snprintf(str, maxlen,
+ "Unknown EC version, returned error: %d\n",
+ msg->result);
+ goto exit;
+ }
}
resp = (struct ec_response_get_version *)msg->data;
@@ -92,10 +101,15 @@ static int cros_ec_chardev_mkbp_event(struct notifier_block *nb,
{
struct chardev_priv *priv = container_of(nb, struct chardev_priv,
notifier);
- struct cros_ec_device *ec_dev = priv->ec_dev;
+ struct cros_ec_device *ec_dev;
struct ec_event *event;
- unsigned long event_bit = 1 << ec_dev->event_data.event_type;
- int total_size = sizeof(*event) + ec_dev->event_size;
+ unsigned long event_bit;
+ int total_size;
+
+ revocable_try_access_or_return_err(priv->rev, ec_dev, NOTIFY_DONE);
+
+ event_bit = 1 << ec_dev->event_data.event_type;
+ total_size = sizeof(*event) + ec_dev->event_size;
if (!(event_bit & priv->event_mask) ||
(priv->event_len + total_size) > CROS_MAX_EVENT_LEN)
@@ -166,7 +180,8 @@ static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
if (!priv)
return -ENOMEM;
- priv->ec_dev = ec_dev;
+ priv->rev = ec_dev->its_rev;
+ revocable_get(priv->rev);
priv->cmd_offset = ec->cmd_offset;
filp->private_data = priv;
INIT_LIST_HEAD(&priv->events);
@@ -178,6 +193,7 @@ static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
&priv->notifier);
if (ret) {
dev_err(ec_dev->dev, "failed to register event notifier\n");
+ revocable_put(priv->rev);
kfree(priv);
}
@@ -251,11 +267,13 @@ static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
static int cros_ec_chardev_release(struct inode *inode, struct file *filp)
{
struct chardev_priv *priv = filp->private_data;
- struct cros_ec_device *ec_dev = priv->ec_dev;
+ struct cros_ec_device *ec_dev;
struct ec_event *event, *e;
- blocking_notifier_chain_unregister(&ec_dev->event_notifier,
- &priv->notifier);
+ revocable_try_access_or_skip_scoped(priv->rev, ec_dev)
+ blocking_notifier_chain_unregister(&ec_dev->event_notifier,
+ &priv->notifier);
+ revocable_put(priv->rev);
list_for_each_entry_safe(event, e, &priv->events, node) {
list_del(&event->node);
@@ -273,6 +291,7 @@ static long cros_ec_chardev_ioctl_xcmd(struct chardev_priv *priv, void __user *a
{
struct cros_ec_command *s_cmd;
struct cros_ec_command u_cmd;
+ struct cros_ec_device *ec_dev;
long ret;
if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
@@ -299,10 +318,17 @@ static long cros_ec_chardev_ioctl_xcmd(struct chardev_priv *priv, void __user *a
}
s_cmd->command += priv->cmd_offset;
- ret = cros_ec_cmd_xfer(priv->ec_dev, s_cmd);
- /* Only copy data to userland if data was received. */
- if (ret < 0)
- goto exit;
+ revocable_try_access_with_scoped(priv->rev, ec_dev) {
+ if (!ec_dev) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ ret = cros_ec_cmd_xfer(ec_dev, s_cmd);
+ /* Only copy data to userland if data was received. */
+ if (ret < 0)
+ goto exit;
+ }
if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize))
ret = -EFAULT;
@@ -313,10 +339,12 @@ static long cros_ec_chardev_ioctl_xcmd(struct chardev_priv *priv, void __user *a
static long cros_ec_chardev_ioctl_readmem(struct chardev_priv *priv, void __user *arg)
{
- struct cros_ec_device *ec_dev = priv->ec_dev;
+ struct cros_ec_device *ec_dev;
struct cros_ec_readmem s_mem = { };
long num;
+ revocable_try_access_or_return(priv->rev, ec_dev);
+
/* Not every platform supports direct reads */
if (!ec_dev->cmd_readmem)
return -ENOTTY;
@@ -370,11 +398,25 @@ static const struct file_operations chardev_fops = {
#endif
};
+static void cros_ec_chardev_free(void *data)
+{
+ struct revocable *rev = data;
+
+ revocable_put(rev);
+}
+
static int cros_ec_chardev_probe(struct platform_device *pdev)
{
struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
+ struct revocable *rev = ec->ec_dev->its_rev;
struct miscdevice *misc;
+ int ret;
+
+ revocable_get(rev);
+ ret = devm_add_action_or_reset(&pdev->dev, cros_ec_chardev_free, rev);
+ if (ret)
+ return ret;
/* Create a char device: we want to create it anew */
misc = devm_kzalloc(&pdev->dev, sizeof(*misc), GFP_KERNEL);
--
2.51.0
prev parent reply other threads:[~2026-05-13 9:12 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-13 9:10 [PATCH v11 0/5] drivers/base: Introduce revocable Tzung-Bi Shih
2026-05-13 9:10 ` [PATCH v11 1/5] revocable: Revocable resource management Tzung-Bi Shih
2026-05-13 9:10 ` [PATCH v11 2/5] revocable: Add KUnit test cases Tzung-Bi Shih
2026-05-13 9:10 ` [PATCH v11 3/5] gpio: Leverage revocable for accessing struct gpio_chip Tzung-Bi Shih
2026-05-13 9:10 ` [PATCH v11 4/5] platform/chrome: Protect cros_ec_device lifecycle with revocable Tzung-Bi Shih
2026-05-13 11:51 ` Jason Gunthorpe
2026-05-14 3:34 ` Tzung-Bi Shih
2026-05-14 16:00 ` Jason Gunthorpe
2026-05-13 9:10 ` Tzung-Bi Shih [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260513091043.6766-6-tzungbi@kernel.org \
--to=tzungbi@kernel.org \
--cc=arnd@arndb.de \
--cc=bleung@chromium.org \
--cc=brgl@kernel.org \
--cc=chrome-platform@lists.linux.dev \
--cc=corbet@lwn.net \
--cc=dakr@kernel.org \
--cc=driver-core@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=jgg@nvidia.com \
--cc=johan@kernel.org \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linusw@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=paulmck@kernel.org \
--cc=rafael@kernel.org \
--cc=shuah@kernel.org \
--cc=wsa+renesas@sang-engineering.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.