diff mbox

[v2,01/16] chardev: add helper function to register char devs with a struct device

Message ID 1488091097-12328-2-git-send-email-logang@deltatee.com
State Superseded
Headers show

Commit Message

Logan Gunthorpe Feb. 26, 2017, 6:38 a.m. UTC
Credit for this patch goes is shared with Dan Williams [1]. I've
taken things one step further to make the helper function more
useful and clean up calling code.

There's a common pattern in the kernel whereby a struct cdev is placed
in a structure along side a struct device which manages the life-cycle
of both. In the naive approach, the reference counting is broken and
the struct device can free everything before the chardev code
is entirely released.

Many developers have solved this problem by linking the internal kobjs
in this fashion:

cdev.kobj.parent = &parent_dev.kobj;

The cdev code explicitly gets and puts a reference to it's kobj parent.
So this seems like it was intended to be used this way. Dmitrty Torokhov
first put this in place in 2012 with this commit:

2f0157f char_dev: pin parent kobject

and the first instance of the fix was then done in the input subsystem
in the following commit:

4a215aa Input: fix use-after-free introduced with dynamic minor changes

Subsequently over the years, however, this issue seems to have tripped
up multiple developers independently. For example, see these commits:

0d5b7da iio: Prevent race between IIO chardev opening and IIO device
(by Lars-Peter Clausen in 2013)

ba0ef85 tpm: Fix initialization of the cdev
(by Jason Gunthorpe in 2015)

5b28dde [media] media: fix use-after-free in cdev_put() when app exits
after driver unbind
(by Shauh Khan in 2016)

This technique is similarly done in at least 15 places within the kernel
and probably should have been done so in another, at least, 5 places.
The kobj line also looks very suspect in that one would not expect
drivers to have to mess with kobject internals in this way.
Even highly experienced kernel developers can be surprised by this
code, as seen in [2].

To help alleviate this situation, and hopefully prevent future
wasted effort on this problem, this patch introduces a helper function
to register a char device along with its parent struct device.
This creates a more regular API for tying a char device to its parent
without the developer having to set members in the underlying kobject.

This patch introduce cdev_device_add and cdev_device_del which
replaces a common pattern including setting the kobj parent, calling
cdev_add and then calling device_add. It also introduces cdev_set_parent
for the few cases that set the kobject parent without using device_add.

[1] https://lkml.org/lkml/2017/2/13/700
[2] https://lkml.org/lkml/2017/2/10/370

Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 fs/char_dev.c        | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/cdev.h |  4 ++++
 2 files changed, 71 insertions(+)

Comments

Dan Williams Feb. 26, 2017, 6:21 p.m. UTC | #1
On Sat, Feb 25, 2017 at 10:38 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
> Credit for this patch goes is shared with Dan Williams [1]. I've
> taken things one step further to make the helper function more
> useful and clean up calling code.
>
> There's a common pattern in the kernel whereby a struct cdev is placed
> in a structure along side a struct device which manages the life-cycle
> of both. In the naive approach, the reference counting is broken and
> the struct device can free everything before the chardev code
> is entirely released.
>
> Many developers have solved this problem by linking the internal kobjs
> in this fashion:
>
> cdev.kobj.parent = &parent_dev.kobj;
>
> The cdev code explicitly gets and puts a reference to it's kobj parent.
> So this seems like it was intended to be used this way. Dmitrty Torokhov
> first put this in place in 2012 with this commit:
>
> 2f0157f char_dev: pin parent kobject
>
> and the first instance of the fix was then done in the input subsystem
> in the following commit:
>
> 4a215aa Input: fix use-after-free introduced with dynamic minor changes
>
> Subsequently over the years, however, this issue seems to have tripped
> up multiple developers independently. For example, see these commits:
>
> 0d5b7da iio: Prevent race between IIO chardev opening and IIO device
> (by Lars-Peter Clausen in 2013)
>
> ba0ef85 tpm: Fix initialization of the cdev
> (by Jason Gunthorpe in 2015)
>
> 5b28dde [media] media: fix use-after-free in cdev_put() when app exits
> after driver unbind
> (by Shauh Khan in 2016)
>
> This technique is similarly done in at least 15 places within the kernel
> and probably should have been done so in another, at least, 5 places.
> The kobj line also looks very suspect in that one would not expect
> drivers to have to mess with kobject internals in this way.
> Even highly experienced kernel developers can be surprised by this
> code, as seen in [2].

Nice history!

>
> To help alleviate this situation, and hopefully prevent future
> wasted effort on this problem, this patch introduces a helper function
> to register a char device along with its parent struct device.
> This creates a more regular API for tying a char device to its parent
> without the developer having to set members in the underlying kobject.
>
> This patch introduce cdev_device_add and cdev_device_del which
> replaces a common pattern including setting the kobj parent, calling
> cdev_add and then calling device_add. It also introduces cdev_set_parent
> for the few cases that set the kobject parent without using device_add.
>
> [1] https://lkml.org/lkml/2017/2/13/700
> [2] https://lkml.org/lkml/2017/2/10/370
>
> Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  fs/char_dev.c        | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/cdev.h |  4 ++++
>  2 files changed, 71 insertions(+)
>
> diff --git a/fs/char_dev.c b/fs/char_dev.c
> index 44a240c..471d480 100644
> --- a/fs/char_dev.c
> +++ b/fs/char_dev.c
> @@ -471,6 +471,70 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
>         return 0;
>  }
>
> +/**
> + * cdev_set_parent() - set the parent kobject for a char device
> + * @p: the cdev structure
> + * @kobj: the kobject to take a reference to
> + *
> + * cdev_set_parent() sets a parent kobject which will be referenced
> + * appropriately so the parent is not freed before the cdev. This
> + * should be called before cdev_add.
> + */
> +void cdev_set_parent(struct cdev *p, struct kobject *kobj)
> +{
> +       WARN_ON(!kobj->state_initialized);
> +       p->kobj.parent = kobj;
> +}
> +
> +/**
> + * cdev_device_add() - add a char device and it's corresponding
> + *     struct device, linkink

"add a cdev and it's corresponding struct device"

> + * @dev: the device structure
> + * @cdev: the cdev structure
> + *
> + * cdev_device_add() adds the char device represented by @cdev to the system,
> + * just as cdev_add does. It then adds @dev to the system using device_add
> + * The dev_t for the char device will be taken from the struct device which
> + * needs to be initialized first. This helper function correctly takes a
> + * reference to the parent device so the parent will not get released until
> + * all references to the cdev are released.
> + *
> + * This function should be used whenever the struct cdev and the
> + * struct device are members of the same structure whose lifetime is
> + * managed by the struct device.
> + */

Perhaps add a note here that userspace may have invoked file
operations between cdev_add() and a failing device_add(), so
additional cleanup beyond put_device() (like mmap invalidation) might
be needed. That can be a later follow-on patch.

> +int cdev_device_add(struct cdev *cdev, struct device *dev)
> +{
> +       int rc = 0;
> +
> +       cdev_set_parent(cdev, &dev->kobj);
> +
> +       rc = cdev_add(cdev, dev->devt, 1);
> +       if (rc)
> +               return rc;
> +
> +       rc = device_add(dev);
> +       if (rc)
> +               cdev_del(cdev);
> +
> +       return rc;
> +}
> +
Hans Verkuil (hansverk) Feb. 27, 2017, 9:01 a.m. UTC | #2
On 02/26/2017 07:38 AM, Logan Gunthorpe wrote:
> Credit for this patch goes is shared with Dan Williams [1]. I've
> taken things one step further to make the helper function more
> useful and clean up calling code.
>
> There's a common pattern in the kernel whereby a struct cdev is placed
> in a structure along side a struct device which manages the life-cycle
> of both. In the naive approach, the reference counting is broken and
> the struct device can free everything before the chardev code
> is entirely released.
>
> Many developers have solved this problem by linking the internal kobjs
> in this fashion:
>
> cdev.kobj.parent = &parent_dev.kobj;
>
> The cdev code explicitly gets and puts a reference to it's kobj parent.
> So this seems like it was intended to be used this way. Dmitrty Torokhov
> first put this in place in 2012 with this commit:
>
> 2f0157f char_dev: pin parent kobject
>
> and the first instance of the fix was then done in the input subsystem
> in the following commit:
>
> 4a215aa Input: fix use-after-free introduced with dynamic minor changes
>
> Subsequently over the years, however, this issue seems to have tripped
> up multiple developers independently. For example, see these commits:
>
> 0d5b7da iio: Prevent race between IIO chardev opening and IIO device
> (by Lars-Peter Clausen in 2013)
>
> ba0ef85 tpm: Fix initialization of the cdev
> (by Jason Gunthorpe in 2015)
>
> 5b28dde [media] media: fix use-after-free in cdev_put() when app exits
> after driver unbind
> (by Shauh Khan in 2016)
>
> This technique is similarly done in at least 15 places within the kernel
> and probably should have been done so in another, at least, 5 places.
> The kobj line also looks very suspect in that one would not expect
> drivers to have to mess with kobject internals in this way.
> Even highly experienced kernel developers can be surprised by this
> code, as seen in [2].
>
> To help alleviate this situation, and hopefully prevent future
> wasted effort on this problem, this patch introduces a helper function
> to register a char device along with its parent struct device.
> This creates a more regular API for tying a char device to its parent
> without the developer having to set members in the underlying kobject.
>
> This patch introduce cdev_device_add and cdev_device_del which
> replaces a common pattern including setting the kobj parent, calling
> cdev_add and then calling device_add. It also introduces cdev_set_parent
> for the few cases that set the kobject parent without using device_add.
>
> [1] https://lkml.org/lkml/2017/2/13/700
> [2] https://lkml.org/lkml/2017/2/10/370
>
> Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com>

Nice!

Regards,

	Hans

> ---
>  fs/char_dev.c        | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/cdev.h |  4 ++++
>  2 files changed, 71 insertions(+)
>
> diff --git a/fs/char_dev.c b/fs/char_dev.c
> index 44a240c..471d480 100644
> --- a/fs/char_dev.c
> +++ b/fs/char_dev.c
> @@ -471,6 +471,70 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
>  	return 0;
>  }
>
> +/**
> + * cdev_set_parent() - set the parent kobject for a char device
> + * @p: the cdev structure
> + * @kobj: the kobject to take a reference to
> + *
> + * cdev_set_parent() sets a parent kobject which will be referenced
> + * appropriately so the parent is not freed before the cdev. This
> + * should be called before cdev_add.
> + */
> +void cdev_set_parent(struct cdev *p, struct kobject *kobj)
> +{
> +	WARN_ON(!kobj->state_initialized);
> +	p->kobj.parent = kobj;
> +}
> +
> +/**
> + * cdev_device_add() - add a char device and it's corresponding
> + *	struct device, linkink
> + * @dev: the device structure
> + * @cdev: the cdev structure
> + *
> + * cdev_device_add() adds the char device represented by @cdev to the system,
> + * just as cdev_add does. It then adds @dev to the system using device_add
> + * The dev_t for the char device will be taken from the struct device which
> + * needs to be initialized first. This helper function correctly takes a
> + * reference to the parent device so the parent will not get released until
> + * all references to the cdev are released.
> + *
> + * This function should be used whenever the struct cdev and the
> + * struct device are members of the same structure whose lifetime is
> + * managed by the struct device.
> + */
> +int cdev_device_add(struct cdev *cdev, struct device *dev)
> +{
> +	int rc = 0;
> +
> +	cdev_set_parent(cdev, &dev->kobj);
> +
> +	rc = cdev_add(cdev, dev->devt, 1);
> +	if (rc)
> +		return rc;
> +
> +	rc = device_add(dev);
> +	if (rc)
> +		cdev_del(cdev);
> +
> +	return rc;
> +}
> +
> +/**
> + * cdev_device_del() - inverse of cdev_device_add
> + * @dev: the device structure
> + * @cdev: the cdev structure
> + *
> + * cdev_device_del() is a helper function to call cdev_del
> + * and device_del. It should be used whenever cdev_device_add
> + * is used.
> + */
> +void cdev_device_del(struct cdev *cdev, struct device *dev)
> +{
> +	device_del(dev);
> +	cdev_del(cdev);
> +}
> +
>  static void cdev_unmap(dev_t dev, unsigned count)
>  {
>  	kobj_unmap(cdev_map, dev, count);
> @@ -570,5 +634,8 @@ EXPORT_SYMBOL(cdev_init);
>  EXPORT_SYMBOL(cdev_alloc);
>  EXPORT_SYMBOL(cdev_del);
>  EXPORT_SYMBOL(cdev_add);
> +EXPORT_SYMBOL(cdev_set_parent);
> +EXPORT_SYMBOL(cdev_device_add);
> +EXPORT_SYMBOL(cdev_device_del);
>  EXPORT_SYMBOL(__register_chrdev);
>  EXPORT_SYMBOL(__unregister_chrdev);
> diff --git a/include/linux/cdev.h b/include/linux/cdev.h
> index f876361..4c99579 100644
> --- a/include/linux/cdev.h
> +++ b/include/linux/cdev.h
> @@ -26,6 +26,10 @@ void cdev_put(struct cdev *p);
>
>  int cdev_add(struct cdev *, dev_t, unsigned);
>
> +void cdev_set_parent(struct cdev *p, struct kobject *kobj);
> +int cdev_device_add(struct cdev *cdev, struct device *dev);
> +void cdev_device_del(struct cdev *cdev, struct device *dev);
> +
>  void cdev_del(struct cdev *);
>
>  void cd_forget(struct inode *);
>
Alexandre Belloni Feb. 27, 2017, 9:47 a.m. UTC | #3
On 25/02/2017 at 23:38:02 -0700, Logan Gunthorpe wrote:
> Credit for this patch goes is shared with Dan Williams [1]. I've
> taken things one step further to make the helper function more
> useful and clean up calling code.
> 
> There's a common pattern in the kernel whereby a struct cdev is placed
> in a structure along side a struct device which manages the life-cycle
> of both. In the naive approach, the reference counting is broken and
> the struct device can free everything before the chardev code
> is entirely released.
> 
> Many developers have solved this problem by linking the internal kobjs
> in this fashion:
> 
> cdev.kobj.parent = &parent_dev.kobj;
> 
> The cdev code explicitly gets and puts a reference to it's kobj parent.
> So this seems like it was intended to be used this way. Dmitrty Torokhov
> first put this in place in 2012 with this commit:
> 
> 2f0157f char_dev: pin parent kobject
> 
> and the first instance of the fix was then done in the input subsystem
> in the following commit:
> 
> 4a215aa Input: fix use-after-free introduced with dynamic minor changes
> 
> Subsequently over the years, however, this issue seems to have tripped
> up multiple developers independently. For example, see these commits:
> 
> 0d5b7da iio: Prevent race between IIO chardev opening and IIO device
> (by Lars-Peter Clausen in 2013)
> 
> ba0ef85 tpm: Fix initialization of the cdev
> (by Jason Gunthorpe in 2015)
> 
> 5b28dde [media] media: fix use-after-free in cdev_put() when app exits
> after driver unbind
> (by Shauh Khan in 2016)
> 
> This technique is similarly done in at least 15 places within the kernel
> and probably should have been done so in another, at least, 5 places.
> The kobj line also looks very suspect in that one would not expect
> drivers to have to mess with kobject internals in this way.
> Even highly experienced kernel developers can be surprised by this
> code, as seen in [2].
> 
> To help alleviate this situation, and hopefully prevent future
> wasted effort on this problem, this patch introduces a helper function
> to register a char device along with its parent struct device.
> This creates a more regular API for tying a char device to its parent
> without the developer having to set members in the underlying kobject.
> 
> This patch introduce cdev_device_add and cdev_device_del which
> replaces a common pattern including setting the kobj parent, calling
> cdev_add and then calling device_add. It also introduces cdev_set_parent
> for the few cases that set the kobject parent without using device_add.
> 
> [1] https://lkml.org/lkml/2017/2/13/700
> [2] https://lkml.org/lkml/2017/2/10/370
> 
> Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Jason Gunthorpe Feb. 27, 2017, 4:56 p.m. UTC | #4
On Sun, Feb 26, 2017 at 10:21:25AM -0800, Dan Williams wrote:
> > + * cdev_device_add() adds the char device represented by @cdev to the system,
> > + * just as cdev_add does. It then adds @dev to the system using device_add
> > + * The dev_t for the char device will be taken from the struct device which
> > + * needs to be initialized first. This helper function correctly takes a
> > + * reference to the parent device so the parent will not get released until
> > + * all references to the cdev are released.
> > + *
> > + * This function should be used whenever the struct cdev and the
> > + * struct device are members of the same structure whose lifetime is
> > + * managed by the struct device.
> > + */
> 
> Perhaps add a note here that userspace may have invoked file
> operations between cdev_add() and a failing device_add(), so
> additional cleanup beyond put_device() (like mmap invalidation) might
> be needed. That can be a later follow-on patch.

Yes please, that is way too subtle. Suggest:

NOTE: Callers must assume that userspace was able to open the cdev and
can call cdev fops callbacks at any time, even if this function fails.

I would also add a note to cdev_device_del:

NOTE: This guarantees that associated sysfs callbacks are not running
or runnable, however any open cdevs will remain and their fops remain
callable even after this returns.

Since I have seen a lot of confusion on that point as well..

Jason
Logan Gunthorpe Feb. 28, 2017, 3:27 a.m. UTC | #5
On 27/02/17 09:56 AM, Jason Gunthorpe wrote:
> Yes please, that is way too subtle. Suggest:
> 
> NOTE: Callers must assume that userspace was able to open the cdev and
> can call cdev fops callbacks at any time, even if this function fails.
> 
> I would also add a note to cdev_device_del:
> 
> NOTE: This guarantees that associated sysfs callbacks are not running
> or runnable, however any open cdevs will remain and their fops remain
> callable even after this returns.
> 
> Since I have seen a lot of confusion on that point as well..

Sure, I'll add that verbiage to a v3 sometime after rc1.

Logan
Dan Williams Feb. 28, 2017, 3:36 a.m. UTC | #6
On Mon, Feb 27, 2017 at 7:27 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
>
>
> On 27/02/17 09:56 AM, Jason Gunthorpe wrote:
>> Yes please, that is way too subtle. Suggest:
>>
>> NOTE: Callers must assume that userspace was able to open the cdev and
>> can call cdev fops callbacks at any time, even if this function fails.
>>
>> I would also add a note to cdev_device_del:
>>
>> NOTE: This guarantees that associated sysfs callbacks are not running
>> or runnable, however any open cdevs will remain and their fops remain
>> callable even after this returns.
>>
>> Since I have seen a lot of confusion on that point as well..
>
> Sure, I'll add that verbiage to a v3 sometime after rc1.

It would also be great to patch1 into a stable non-rebasing git tree
so it can be used as a baseline for other 4.12 targeted development.
The device-dax patches in particular collide with some pending
development.
gregkh@linuxfoundation.org Feb. 28, 2017, 5:31 a.m. UTC | #7
On Mon, Feb 27, 2017 at 07:36:09PM -0800, Dan Williams wrote:
> On Mon, Feb 27, 2017 at 7:27 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
> >
> >
> > On 27/02/17 09:56 AM, Jason Gunthorpe wrote:
> >> Yes please, that is way too subtle. Suggest:
> >>
> >> NOTE: Callers must assume that userspace was able to open the cdev and
> >> can call cdev fops callbacks at any time, even if this function fails.
> >>
> >> I would also add a note to cdev_device_del:
> >>
> >> NOTE: This guarantees that associated sysfs callbacks are not running
> >> or runnable, however any open cdevs will remain and their fops remain
> >> callable even after this returns.
> >>
> >> Since I have seen a lot of confusion on that point as well..
> >
> > Sure, I'll add that verbiage to a v3 sometime after rc1.
> 
> It would also be great to patch1 into a stable non-rebasing git tree
> so it can be used as a baseline for other 4.12 targeted development.
> The device-dax patches in particular collide with some pending
> development.

I'll do that once -rc1 is out, I can't take any new patches right now :)

thanks,

greg k-h
diff mbox

Patch

diff --git a/fs/char_dev.c b/fs/char_dev.c
index 44a240c..471d480 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -471,6 +471,70 @@  int cdev_add(struct cdev *p, dev_t dev, unsigned count)
 	return 0;
 }
 
+/**
+ * cdev_set_parent() - set the parent kobject for a char device
+ * @p: the cdev structure
+ * @kobj: the kobject to take a reference to
+ *
+ * cdev_set_parent() sets a parent kobject which will be referenced
+ * appropriately so the parent is not freed before the cdev. This
+ * should be called before cdev_add.
+ */
+void cdev_set_parent(struct cdev *p, struct kobject *kobj)
+{
+	WARN_ON(!kobj->state_initialized);
+	p->kobj.parent = kobj;
+}
+
+/**
+ * cdev_device_add() - add a char device and it's corresponding
+ *	struct device, linkink
+ * @dev: the device structure
+ * @cdev: the cdev structure
+ *
+ * cdev_device_add() adds the char device represented by @cdev to the system,
+ * just as cdev_add does. It then adds @dev to the system using device_add
+ * The dev_t for the char device will be taken from the struct device which
+ * needs to be initialized first. This helper function correctly takes a
+ * reference to the parent device so the parent will not get released until
+ * all references to the cdev are released.
+ *
+ * This function should be used whenever the struct cdev and the
+ * struct device are members of the same structure whose lifetime is
+ * managed by the struct device.
+ */
+int cdev_device_add(struct cdev *cdev, struct device *dev)
+{
+	int rc = 0;
+
+	cdev_set_parent(cdev, &dev->kobj);
+
+	rc = cdev_add(cdev, dev->devt, 1);
+	if (rc)
+		return rc;
+
+	rc = device_add(dev);
+	if (rc)
+		cdev_del(cdev);
+
+	return rc;
+}
+
+/**
+ * cdev_device_del() - inverse of cdev_device_add
+ * @dev: the device structure
+ * @cdev: the cdev structure
+ *
+ * cdev_device_del() is a helper function to call cdev_del
+ * and device_del. It should be used whenever cdev_device_add
+ * is used.
+ */
+void cdev_device_del(struct cdev *cdev, struct device *dev)
+{
+	device_del(dev);
+	cdev_del(cdev);
+}
+
 static void cdev_unmap(dev_t dev, unsigned count)
 {
 	kobj_unmap(cdev_map, dev, count);
@@ -570,5 +634,8 @@  EXPORT_SYMBOL(cdev_init);
 EXPORT_SYMBOL(cdev_alloc);
 EXPORT_SYMBOL(cdev_del);
 EXPORT_SYMBOL(cdev_add);
+EXPORT_SYMBOL(cdev_set_parent);
+EXPORT_SYMBOL(cdev_device_add);
+EXPORT_SYMBOL(cdev_device_del);
 EXPORT_SYMBOL(__register_chrdev);
 EXPORT_SYMBOL(__unregister_chrdev);
diff --git a/include/linux/cdev.h b/include/linux/cdev.h
index f876361..4c99579 100644
--- a/include/linux/cdev.h
+++ b/include/linux/cdev.h
@@ -26,6 +26,10 @@  void cdev_put(struct cdev *p);
 
 int cdev_add(struct cdev *, dev_t, unsigned);
 
+void cdev_set_parent(struct cdev *p, struct kobject *kobj);
+int cdev_device_add(struct cdev *cdev, struct device *dev);
+void cdev_device_del(struct cdev *cdev, struct device *dev);
+
 void cdev_del(struct cdev *);
 
 void cd_forget(struct inode *);