From: Tzung-Bi Shih <tzungbi@kernel.org>
To: Greg KH <gregkh@linuxfoundation.org>
Cc: bleung@chromium.org, chrome-platform@lists.linux.dev,
dawidn@google.com, stable@vger.kernel.org
Subject: Re: [PATCH 2/2] platform/chrome: cros_ec_chardev: Fix UAF of ec_dev
Date: Thu, 3 Jul 2025 13:14:42 +0000 [thread overview]
Message-ID: <aGaCQuvwPjDIaH6W@google.com> (raw)
In-Reply-To: <2025070320-gathering-smitten-8909@gregkh>
On Thu, Jul 03, 2025 at 01:52:02PM +0200, Greg KH wrote:
> On Thu, Jul 03, 2025 at 11:35:09AM +0000, Tzung-Bi Shih wrote:
> But yes, one that people have been talking about and discussing generic
> ways of solving for years now, you have seen the plumbers talks about
> it, right?
Will check them.
> > @@ -31,7 +34,14 @@
> > /* Arbitrary bounded size for the event queue */
> > #define CROS_MAX_EVENT_LEN PAGE_SIZE
> >
> > +/* This protects 'chardev_list' */
> > +static DEFINE_MUTEX(chardev_lock);
> > +static LIST_HEAD(chardev_list);
>
> Having a static list of chardevices feels very odd and driver-specific,
> right
The `chardev_list` is for recording all opened instances. Adding/removing
entries in the .open()/.release() fops. The `chardev_lock` is for protecting
from accessing the list simultaneously.
They are statically allocated because they can't follow the lifecycle of
neither the platform_device (e.g. can be gone after unbinding the driver)
nor the chardev (e.g. can be gone after closing the file).
Side note: realized an issue in current version: in the .remove() of
platform_driver, it unconditionally resets `ec_dev` for all opened instances.
> > +
> > struct chardev_priv {
> > + struct list_head list;
> > + /* This protects 'ec_dev' */
> > + struct mutex lock;
>
> Protects it from what?
>
> You now have two locks in play, one for the structure itself, and one
> for the list. Yet how do they interact as the list is a list of the
> objects which have their own lock?
>
> Are you _SURE_ you need two locks here? If so, you need to document
> these really well.
`struct chardev_priv` is bound to chardev's lifecycle. It is allocated in
the .open() fops; and freed in the .release() fops. The `lock` is for
protecting from accessing the `ec_dev` simultaneously (one is chardev
itself, another one is the .remove() of platform_driver).
> > @@ -341,16 +364,20 @@ static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd,
> > unsigned long arg)
> > {
> > struct chardev_priv *priv = filp->private_data;
> > - struct cros_ec_dev *ec = priv->ec_dev;
> >
> > if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
> > return -ENOTTY;
> >
> > + scoped_guard(mutex, &priv->lock) {
> > + if (!priv->ec_dev)
> > + return -ENODEV;
> > + }
>
> What prevents ec_dev from changing now, after you have just checked it?
> This feels very wrong as:
Ah, yes. I did it wrong. The `priv->lock` should be held at least for the
following cros_ec_chardev_ioctl_xcmd() and cros_ec_chardev_ioctl_readmem().
> > +
> > switch (cmd) {
> > case CROS_EC_DEV_IOCXCMD:
> > - return cros_ec_chardev_ioctl_xcmd(ec, (void __user *)arg);
> > + return cros_ec_chardev_ioctl_xcmd(priv->ec_dev, (void __user *)arg);
>
> Look, it could have gone away here, right? If not, how?
Yes, I did it wrong. The lock should be still held for them.
> > @@ -394,8 +421,28 @@ static int cros_ec_chardev_probe(struct platform_device *pdev)
> > static void cros_ec_chardev_remove(struct platform_device *pdev)
> > {
> > struct miscdevice *misc = dev_get_drvdata(&pdev->dev);
> > + struct chardev_priv *priv;
> >
> > + /*
> > + * Must deregister the misc device first so that the following
> > + * open fops get handled correctly.
> > + *
> > + * misc device is serialized by `misc_mtx`.
> > + * 1) If misc_deregister() gets the lock earlier than misc_open(),
> > + * the open fops won't be called as the corresponding misc
> > + * device is already destroyed.
> > + * 2) If misc_open() gets the lock earlier than misc_deregister(),
> > + * the following code block resets the `ec_dev` to prevent
> > + * the rest of fops from accessing the obsolete `ec_dev`.
>
> What "following code block"? What will reset the structure?
+ scoped_guard(mutex, &chardev_lock) {
+ list_for_each_entry(priv, &chardev_list, list) {
+ scoped_guard(mutex, &priv->lock)
+ priv->ec_dev = NULL;
+ }
+ }
next prev parent reply other threads:[~2025-07-03 13:14 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-03 11:35 [PATCH 0/2] platform/chrome: cros_ec_chardev: Fix a possible UAF Tzung-Bi Shih
2025-07-03 11:35 ` [PATCH 1/2] platform/chrome: cros_ec_chardev: Remove redundant struct field Tzung-Bi Shih
2025-07-03 11:35 ` [PATCH 2/2] platform/chrome: cros_ec_chardev: Fix UAF of ec_dev Tzung-Bi Shih
2025-07-03 11:52 ` Greg KH
2025-07-03 13:14 ` Tzung-Bi Shih [this message]
2025-07-03 13:52 ` Greg KH
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=aGaCQuvwPjDIaH6W@google.com \
--to=tzungbi@kernel.org \
--cc=bleung@chromium.org \
--cc=chrome-platform@lists.linux.dev \
--cc=dawidn@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=stable@vger.kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox