public inbox for chrome-platform@lists.linux.dev
 help / color / mirror / Atom feed
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;
+		}
+	}

  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