All of lore.kernel.org
 help / color / mirror / Atom feed
From: Greg KH <gregkh@linuxfoundation.org>
To: Adrian Salido <salidoa@google.com>
Cc: linux-kernel@vger.kernel.org
Subject: Re: [PATCH] driver core: platform: fix race condition with driver_override
Date: Wed, 26 Apr 2017 17:48:12 +0200	[thread overview]
Message-ID: <20170426154812.GA18399@kroah.com> (raw)
In-Reply-To: <CAEP91RET3sKH_77W7=D0Dcj-itN-2eP=qv3+jNndeSvrVk6xWw@mail.gmail.com>

On Wed, Apr 26, 2017 at 07:51:32AM -0700, Adrian Salido wrote:
> > > The driver_override implementation is susceptible to race condition when
> > > different threads are reading vs storing a different driver override.
> > > Add locking to avoid race condition.
> > >
> > > Fixes: 3d713e0e382e ("driver core: platform: add device binding path 'driver_override'")
> > > Cc: stable@vger.kernel.org
> > > Signed-off-by: Adrian Salido <salidoa@google.com>
> > > ---
> > >  drivers/base/platform.c | 11 +++++++++--
> > >  1 file changed, 9 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/base/platform.c b/drivers/base/platform.c
> > > index c2456839214a..493e03fa0e07 100644
> > > --- a/drivers/base/platform.c
> > > +++ b/drivers/base/platform.c
> > > @@ -866,7 +866,7 @@ static ssize_t driver_override_store(struct device *dev,
> > >                                    const char *buf, size_t count)
> > >  {
> > >       struct platform_device *pdev = to_platform_device(dev);
> > > -     char *driver_override, *old = pdev->driver_override, *cp;
> > > +     char *driver_override, *old, *cp;
> > >
> > >       if (count > PATH_MAX)
> > >               return -EINVAL;
> > > @@ -879,12 +879,15 @@ static ssize_t driver_override_store(struct device *dev,
> > >       if (cp)
> > >               *cp = '\0';
> > >
> > > +     device_lock(dev);
> > > +     old = pdev->driver_override;
> > >       if (strlen(driver_override)) {
> > >               pdev->driver_override = driver_override;
> > >       } else {
> > >               kfree(driver_override);
> > >               pdev->driver_override = NULL;
> > >       }
> > > +     device_unlock(dev);
> > >
> > >       kfree(old);
> >
> > Shouldn't you move the lock until after the kfree()?  Or am I missing
> > what the lock is trying to protect here?
> 
> not really, the lock only protecting the variable
> pdev->driver_override. Once the value has changed we no longer care
> about "old" variable

What are you protecting it from?  Being overwritten twice?  Or something
else?

> > >       if (cp)
> > >               *cp = '\0';
> > >
> > > +     device_lock(dev);
> > > +     old = pdev->driver_override;
> > >       if (strlen(driver_override)) {
> > >               pdev->driver_override = driver_override;
> > >       } else {
> > >               kfree(driver_override);
> > >               pdev->driver_override = NULL;
> > >       }
> > > +     device_unlock(dev);
> > >
> > >       kfree(old);
> >
> > >
> > > @@ -895,8 +898,12 @@ static ssize_t driver_override_show(struct device *dev,
> > >                                   struct device_attribute *attr, char *buf)
> > >  {
> > >       struct platform_device *pdev = to_platform_device(dev);
> > > +     ssize_t len;
> > >
> > > -     return sprintf(buf, "%s\n", pdev->driver_override);
> > > +     device_lock(dev);
> > > +     len = sprintf(buf, "%s\n", pdev->driver_override);
> > > +     device_unlock(dev);
> > > +     return len;
> >
> > Why does the show function need to be changed at all?  How can anything
> > "race" here?
> 
> The lock is trying to protect again race between store and show.
> Suppose there are 2 threads:
> 
> Thread1:
>  while (1) {
>    driver_override_store("foo");
>    driver_override_store("");
>  }
> 
> Thread2:
> while (1) driver_override_show();
> 
> Thread 1                                 |         Thread 2
> ----------------------------------------|-----------------------
> old = pdev->driver_override;   |
>                                                 | len = sprintf(buf,
> "%s\n", pdev->driver_override);
>                                                 | /* snprintf starts reading */
> pdev->driver_override =          |
>    driver_override;                    |
> kfree(old);                                | /* use after free before
> snprintf finishes execution */
> 
> Similarly there could be a race between multiple threads doing store
> where memory leaks could happen

Ah, the printing of the string, that makes more sense, thanks, I was
thinking of the assignment of the pointer itself, which is atomic on all
sane platforms.

As writing this is only allowed by root, it's not really a big deal,
I'll queue it up for the next release.

thanks,

greg k-h

      reply	other threads:[~2017-04-26 15:48 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-25 23:55 [PATCH] driver core: platform: fix race condition with driver_override Adrian Salido
2017-04-26 11:57 ` Greg KH
2017-04-26 14:51   ` Adrian Salido
2017-04-26 15:48     ` Greg KH [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=20170426154812.GA18399@kroah.com \
    --to=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=salidoa@google.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.