diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2020-04-23 23:18:49 +0100 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2020-04-23 23:33:58 +0100 |
commit | d58cdcc6e1b8a6cdbf39f5274d1c336052d4e823 (patch) | |
tree | 32d7588abc60636a4d4ca850e60939457542c370 | |
parent | c53be8ef0c3d3bcf3c92c0e932eb0a7332b07b99 (diff) | |
download | linux-stable-queue-d58cdcc6e1b8a6cdbf39f5274d1c336052d4e823.tar.gz |
Add more security fixes and related changes
17 files changed, 2155 insertions, 0 deletions
diff --git a/queue-3.16/chardev-add-helper-function-to-register-char-devs-with-a-struct.patch b/queue-3.16/chardev-add-helper-function-to-register-char-devs-with-a-struct.patch new file mode 100644 index 00000000..0b9bb357 --- /dev/null +++ b/queue-3.16/chardev-add-helper-function-to-register-char-devs-with-a-struct.patch @@ -0,0 +1,208 @@ +From: Logan Gunthorpe <logang@deltatee.com> +Date: Fri, 17 Mar 2017 12:48:08 -0600 +Subject: chardev: add helper function to register char devs with a struct + device + +commit 233ed09d7fdacf592ee91e6c97ce5f4364fbe7c0 upstream. + +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> +Reviewed-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + fs/char_dev.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ + include/linux/cdev.h | 5 +++ + 2 files changed, 91 insertions(+) + +--- a/fs/char_dev.c ++++ b/fs/char_dev.c +@@ -488,6 +488,85 @@ int cdev_add(struct cdev *p, dev_t dev, + 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 helper uses dev->devt for the device number. If it is not set ++ * it will not add the cdev and it will be equivalent to device_add. ++ * ++ * 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. ++ * ++ * 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. ++ */ ++int cdev_device_add(struct cdev *cdev, struct device *dev) ++{ ++ int rc = 0; ++ ++ if (dev->devt) { ++ 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. ++ * ++ * If dev->devt is not set it will not remove the cdev and will be equivalent ++ * to device_del. ++ * ++ * NOTE: This guarantees that associated sysfs callbacks are not running ++ * or runnable, however any cdevs already open will remain and their fops ++ * will still be callable even after this function returns. ++ */ ++void cdev_device_del(struct cdev *cdev, struct device *dev) ++{ ++ device_del(dev); ++ if (dev->devt) ++ cdev_del(cdev); ++} ++ + static void cdev_unmap(dev_t dev, unsigned count) + { + kobj_unmap(cdev_map, dev, count); +@@ -499,6 +578,10 @@ static void cdev_unmap(dev_t dev, unsign + * + * cdev_del() removes @p from the system, possibly freeing the structure + * itself. ++ * ++ * NOTE: This guarantees that cdev device will no longer be able to be ++ * opened, however any cdevs already open will remain and their fops will ++ * still be callable even after cdev_del returns. + */ + void cdev_del(struct cdev *p) + { +@@ -589,6 +672,9 @@ 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); + EXPORT_SYMBOL(directly_mappable_cdev_bdi); +--- a/include/linux/cdev.h ++++ b/include/linux/cdev.h +@@ -4,6 +4,7 @@ + #include <linux/kobject.h> + #include <linux/kdev_t.h> + #include <linux/list.h> ++#include <linux/device.h> + + struct file_operations; + struct inode; +@@ -26,6 +27,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 *); diff --git a/queue-3.16/drivers-media-media-devnode-clear-private_data-before.patch b/queue-3.16/drivers-media-media-devnode-clear-private_data-before.patch new file mode 100644 index 00000000..cdb7af40 --- /dev/null +++ b/queue-3.16/drivers-media-media-devnode-clear-private_data-before.patch @@ -0,0 +1,32 @@ +From: Max Kellermann <max@duempel.org> +Date: Mon, 21 Mar 2016 10:30:28 -0300 +Subject: [media] drivers/media/media-devnode: clear private_data before + put_device() + +commit bf244f665d76d20312c80524689b32a752888838 upstream. + +Callbacks invoked from put_device() may free the struct media_devnode +pointer, so any cleanup needs to be done before put_device(). + +Signed-off-by: Max Kellermann <max@duempel.org> +Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/media/media-devnode.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -197,10 +197,11 @@ static int media_release(struct inode *i + if (mdev->fops->release) + mdev->fops->release(filp); + ++ filp->private_data = NULL; ++ + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + put_device(&mdev->dev); +- filp->private_data = NULL; + return 0; + } + diff --git a/queue-3.16/media-device-dynamically-allocate-struct-media_devnode.patch b/queue-3.16/media-device-dynamically-allocate-struct-media_devnode.patch new file mode 100644 index 00000000..7e4d58e9 --- /dev/null +++ b/queue-3.16/media-device-dynamically-allocate-struct-media_devnode.patch @@ -0,0 +1,241 @@ +From: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Date: Wed, 27 Apr 2016 19:28:26 -0300 +Subject: [media] media-device: dynamically allocate struct media_devnode + +commit a087ce704b802becbb4b0f2a20f2cb3f6911802e upstream. + +struct media_devnode is currently embedded at struct media_device. + +While this works fine during normal usage, it leads to a race +condition during devnode unregister. the problem is that drivers +assume that, after calling media_device_unregister(), the struct +that contains media_device can be freed. This is not true, as it +can't be freed until userspace closes all opened /dev/media devnodes. + +In other words, if the media devnode is still open, and media_device +gets freed, any call to an ioctl will make the core to try to access +struct media_device, with will cause an use-after-free and even GPF. + +Fix this by dynamically allocating the struct media_devnode and only +freeing it when it is safe. + +Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> +[bwh: Backported to 3.16: + - Drop change in au0828 + - Include <linux/slab.h> in media-device.c + - Adjust context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -24,6 +24,7 @@ + #include <linux/export.h> + #include <linux/ioctl.h> + #include <linux/media.h> ++#include <linux/slab.h> + #include <linux/types.h> + + #include <media/media-device.h> +@@ -236,7 +237,7 @@ static long media_device_ioctl(struct fi + unsigned long arg) + { + struct media_devnode *devnode = media_devnode_data(filp); +- struct media_device *dev = to_media_device(devnode); ++ struct media_device *dev = devnode->media_dev; + long ret; + + switch (cmd) { +@@ -305,7 +306,7 @@ static long media_device_compat_ioctl(st + unsigned long arg) + { + struct media_devnode *devnode = media_devnode_data(filp); +- struct media_device *dev = to_media_device(devnode); ++ struct media_device *dev = devnode->media_dev; + long ret; + + switch (cmd) { +@@ -346,7 +347,8 @@ static const struct media_file_operation + static ssize_t show_model(struct device *cd, + struct device_attribute *attr, char *buf) + { +- struct media_device *mdev = to_media_device(to_media_devnode(cd)); ++ struct media_devnode *devnode = to_media_devnode(cd); ++ struct media_device *mdev = devnode->media_dev; + + return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); + } +@@ -374,6 +376,7 @@ static void media_device_release(struct + int __must_check __media_device_register(struct media_device *mdev, + struct module *owner) + { ++ struct media_devnode *devnode; + int ret; + + if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) +@@ -384,17 +387,27 @@ int __must_check __media_device_register + spin_lock_init(&mdev->lock); + mutex_init(&mdev->graph_mutex); + ++ devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); ++ if (!devnode) ++ return -ENOMEM; ++ + /* Register the device node. */ +- mdev->devnode.fops = &media_device_fops; +- mdev->devnode.parent = mdev->dev; +- mdev->devnode.release = media_device_release; +- ret = media_devnode_register(&mdev->devnode, owner); +- if (ret < 0) ++ mdev->devnode = devnode; ++ devnode->fops = &media_device_fops; ++ devnode->parent = mdev->dev; ++ devnode->release = media_device_release; ++ ret = media_devnode_register(mdev, devnode, owner); ++ if (ret < 0) { ++ mdev->devnode = NULL; ++ kfree(devnode); + return ret; ++ } + +- ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); ++ ret = device_create_file(&devnode->dev, &dev_attr_model); + if (ret < 0) { +- media_devnode_unregister(&mdev->devnode); ++ mdev->devnode = NULL; ++ media_devnode_unregister(devnode); ++ kfree(devnode); + return ret; + } + +@@ -415,8 +428,11 @@ void media_device_unregister(struct medi + list_for_each_entry_safe(entity, next, &mdev->entities, list) + media_device_unregister_entity(entity); + +- device_remove_file(&mdev->devnode.dev, &dev_attr_model); +- media_devnode_unregister(&mdev->devnode); ++ /* Check if mdev devnode was registered */ ++ if (media_devnode_is_registered(mdev->devnode)) { ++ device_remove_file(&mdev->devnode->dev, &dev_attr_model); ++ media_devnode_unregister(mdev->devnode); ++ } + } + EXPORT_SYMBOL_GPL(media_device_unregister); + +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -44,6 +44,7 @@ + #include <linux/uaccess.h> + + #include <media/media-devnode.h> ++#include <media/media-device.h> + + #define MEDIA_NUM_DEVICES 256 + #define MEDIA_NAME "media" +@@ -74,6 +75,8 @@ static void media_devnode_release(struct + /* Release media_devnode and perform other cleanups as needed. */ + if (devnode->release) + devnode->release(devnode); ++ ++ kfree(devnode); + } + + static struct bus_type media_bus_type = { +@@ -221,6 +224,7 @@ static const struct file_operations medi + + /** + * media_devnode_register - register a media device node ++ * @media_dev: struct media_device we want to register a device node + * @devnode: media device node structure we want to register + * + * The registration code assigns minor numbers and registers the new device node +@@ -233,7 +237,8 @@ static const struct file_operations medi + * the media_devnode structure is *not* called, so the caller is responsible for + * freeing any data. + */ +-int __must_check media_devnode_register(struct media_devnode *devnode, ++int __must_check media_devnode_register(struct media_device *mdev, ++ struct media_devnode *devnode, + struct module *owner) + { + int minor; +@@ -252,6 +257,7 @@ int __must_check media_devnode_register( + mutex_unlock(&media_devnode_lock); + + devnode->minor = minor; ++ devnode->media_dev = mdev; + + /* Part 2: Initialize and register the character device */ + cdev_init(&devnode->cdev, &media_devnode_fops); +--- a/include/media/media-device.h ++++ b/include/media/media-device.h +@@ -60,7 +60,7 @@ struct device; + struct media_device { + /* dev->driver_data points to this struct. */ + struct device *dev; +- struct media_devnode devnode; ++ struct media_devnode *devnode; + + char model[32]; + char serial[40]; +@@ -84,9 +84,6 @@ struct media_device { + #define MEDIA_DEV_NOTIFY_PRE_LINK_CH 0 + #define MEDIA_DEV_NOTIFY_POST_LINK_CH 1 + +-/* media_devnode to media_device */ +-#define to_media_device(node) container_of(node, struct media_device, devnode) +- + int __must_check __media_device_register(struct media_device *mdev, + struct module *owner); + #define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE) +--- a/drivers/media/usb/uvc/uvc_driver.c ++++ b/drivers/media/usb/uvc/uvc_driver.c +@@ -1640,7 +1640,7 @@ static void uvc_delete(struct uvc_device + if (dev->vdev.dev) + v4l2_device_unregister(&dev->vdev); + #ifdef CONFIG_MEDIA_CONTROLLER +- if (media_devnode_is_registered(&dev->mdev.devnode)) ++ if (media_devnode_is_registered(dev->mdev.devnode)) + media_device_unregister(&dev->mdev); + #endif + +--- a/include/media/media-devnode.h ++++ b/include/media/media-devnode.h +@@ -33,6 +33,8 @@ + #include <linux/device.h> + #include <linux/cdev.h> + ++struct media_device; ++ + /* + * Flag to mark the media_devnode struct as registered. Drivers must not touch + * this flag directly, it will be set and cleared by media_devnode_register and +@@ -63,6 +65,8 @@ struct media_file_operations { + * before registering the node. + */ + struct media_devnode { ++ struct media_device *media_dev; ++ + /* device ops */ + const struct media_file_operations *fops; + +@@ -82,7 +86,8 @@ struct media_devnode { + /* dev to media_devnode */ + #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) + +-int __must_check media_devnode_register(struct media_devnode *devnode, ++int __must_check media_devnode_register(struct media_device *mdev, ++ struct media_devnode *devnode, + struct module *owner); + void media_devnode_unregister(struct media_devnode *devnode); + +@@ -93,6 +98,9 @@ static inline struct media_devnode *medi + + static inline int media_devnode_is_registered(struct media_devnode *devnode) + { ++ if (!devnode) ++ return false; ++ + return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + } + diff --git a/queue-3.16/media-devnode-add-missing-mutex-lock-in-error-handler.patch b/queue-3.16/media-devnode-add-missing-mutex-lock-in-error-handler.patch new file mode 100644 index 00000000..d035fe7c --- /dev/null +++ b/queue-3.16/media-devnode-add-missing-mutex-lock-in-error-handler.patch @@ -0,0 +1,30 @@ +From: Max Kellermann <max@duempel.org> +Date: Mon, 21 Mar 2016 04:33:12 -0700 +Subject: [media] media-devnode: add missing mutex lock in error handler + +commit 88336e174645948da269e1812f138f727cd2896b upstream. + +We should protect the device unregister patch too, at the error +condition. + +Signed-off-by: Max Kellermann <max@duempel.org> +Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/media/media-devnode.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -282,8 +282,11 @@ int __must_check media_devnode_register( + return 0; + + error: ++ mutex_lock(&media_devnode_lock); + cdev_del(&mdev->cdev); + clear_bit(mdev->minor, media_devnode_nums); ++ mutex_unlock(&media_devnode_lock); ++ + return ret; + } + diff --git a/queue-3.16/media-devnode-fix-namespace-mess.patch b/queue-3.16/media-devnode-fix-namespace-mess.patch new file mode 100644 index 00000000..d8db22b2 --- /dev/null +++ b/queue-3.16/media-devnode-fix-namespace-mess.patch @@ -0,0 +1,344 @@ +From: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Date: Wed, 23 Mar 2016 11:22:57 -0300 +Subject: [media] media-devnode: fix namespace mess + +commit 163f1e93e995048b894c5fc86a6034d16beed740 upstream. + +Along all media controller code, "mdev" is used to represent +a pointer to struct media_device, and "devnode" for a pointer +to struct media_devnode. + +However, inside media-devnode.[ch], "mdev" is used to represent +a pointer to struct media_devnode. + +This is very confusing and may lead to development errors. + +So, let's change all occurrences at media-devnode.[ch] to +also use "devnode" for such pointers. + +This patch doesn't make any functional changes. + +Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> +[bwh: Backported to 3.16: adjust filename, context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/media/media-devnode.c | 110 +++++++++++++++++----------------- + include/media/media-devnode.h | 16 ++--- + 2 files changed, 63 insertions(+), 63 deletions(-) + +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -59,21 +59,21 @@ static DECLARE_BITMAP(media_devnode_nums + /* Called when the last user of the media device exits. */ + static void media_devnode_release(struct device *cd) + { +- struct media_devnode *mdev = to_media_devnode(cd); ++ struct media_devnode *devnode = to_media_devnode(cd); + + mutex_lock(&media_devnode_lock); + + /* Delete the cdev on this minor as well */ +- cdev_del(&mdev->cdev); ++ cdev_del(&devnode->cdev); + + /* Mark device node number as free */ +- clear_bit(mdev->minor, media_devnode_nums); ++ clear_bit(devnode->minor, media_devnode_nums); + + mutex_unlock(&media_devnode_lock); + + /* Release media_devnode and perform other cleanups as needed. */ +- if (mdev->release) +- mdev->release(mdev); ++ if (devnode->release) ++ devnode->release(devnode); + } + + static struct bus_type media_bus_type = { +@@ -83,37 +83,37 @@ static struct bus_type media_bus_type = + static ssize_t media_read(struct file *filp, char __user *buf, + size_t sz, loff_t *off) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (!mdev->fops->read) ++ if (!devnode->fops->read) + return -EINVAL; +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return -EIO; +- return mdev->fops->read(filp, buf, sz, off); ++ return devnode->fops->read(filp, buf, sz, off); + } + + static ssize_t media_write(struct file *filp, const char __user *buf, + size_t sz, loff_t *off) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (!mdev->fops->write) ++ if (!devnode->fops->write) + return -EINVAL; +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return -EIO; +- return mdev->fops->write(filp, buf, sz, off); ++ return devnode->fops->write(filp, buf, sz, off); + } + + static unsigned int media_poll(struct file *filp, + struct poll_table_struct *poll) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return POLLERR | POLLHUP; +- if (!mdev->fops->poll) ++ if (!devnode->fops->poll) + return DEFAULT_POLLMASK; +- return mdev->fops->poll(filp, poll); ++ return devnode->fops->poll(filp, poll); + } + + static long +@@ -121,12 +121,12 @@ __media_ioctl(struct file *filp, unsigne + long (*ioctl_func)(struct file *filp, unsigned int cmd, + unsigned long arg)) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + + if (!ioctl_func) + return -ENOTTY; + +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return -EIO; + + return ioctl_func(filp, cmd, arg); +@@ -134,9 +134,9 @@ __media_ioctl(struct file *filp, unsigne + + static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl); ++ return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl); + } + + #ifdef CONFIG_COMPAT +@@ -144,9 +144,9 @@ static long media_ioctl(struct file *fil + static long media_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl); ++ return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl); + } + + #endif /* CONFIG_COMPAT */ +@@ -154,7 +154,7 @@ static long media_compat_ioctl(struct fi + /* Override for the open function */ + static int media_open(struct inode *inode, struct file *filp) + { +- struct media_devnode *mdev; ++ struct media_devnode *devnode; + int ret; + + /* Check if the media device is available. This needs to be done with +@@ -164,23 +164,23 @@ static int media_open(struct inode *inod + * a crash. + */ + mutex_lock(&media_devnode_lock); +- mdev = container_of(inode->i_cdev, struct media_devnode, cdev); ++ devnode = container_of(inode->i_cdev, struct media_devnode, cdev); + /* return ENXIO if the media device has been removed + already or if it is not registered anymore. */ +- if (!media_devnode_is_registered(mdev)) { ++ if (!media_devnode_is_registered(devnode)) { + mutex_unlock(&media_devnode_lock); + return -ENXIO; + } + /* and increase the device refcount */ +- get_device(&mdev->dev); ++ get_device(&devnode->dev); + mutex_unlock(&media_devnode_lock); + +- filp->private_data = mdev; ++ filp->private_data = devnode; + +- if (mdev->fops->open) { +- ret = mdev->fops->open(filp); ++ if (devnode->fops->open) { ++ ret = devnode->fops->open(filp); + if (ret) { +- put_device(&mdev->dev); ++ put_device(&devnode->dev); + filp->private_data = NULL; + return ret; + } +@@ -192,16 +192,16 @@ static int media_open(struct inode *inod + /* Override for the release function */ + static int media_release(struct inode *inode, struct file *filp) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (mdev->fops->release) +- mdev->fops->release(filp); ++ if (devnode->fops->release) ++ devnode->fops->release(filp); + + filp->private_data = NULL; + + /* decrease the refcount unconditionally since the release() + return value is ignored. */ +- put_device(&mdev->dev); ++ put_device(&devnode->dev); + return 0; + } + +@@ -221,7 +221,7 @@ static const struct file_operations medi + + /** + * media_devnode_register - register a media device node +- * @mdev: media device node structure we want to register ++ * @devnode: media device node structure we want to register + * + * The registration code assigns minor numbers and registers the new device node + * with the kernel. An error is returned if no free minor number can be found, +@@ -233,7 +233,7 @@ static const struct file_operations medi + * the media_devnode structure is *not* called, so the caller is responsible for + * freeing any data. + */ +-int __must_check media_devnode_register(struct media_devnode *mdev, ++int __must_check media_devnode_register(struct media_devnode *devnode, + struct module *owner) + { + int minor; +@@ -251,40 +251,40 @@ int __must_check media_devnode_register( + set_bit(minor, media_devnode_nums); + mutex_unlock(&media_devnode_lock); + +- mdev->minor = minor; ++ devnode->minor = minor; + + /* Part 2: Initialize and register the character device */ +- cdev_init(&mdev->cdev, &media_devnode_fops); +- mdev->cdev.owner = owner; ++ cdev_init(&devnode->cdev, &media_devnode_fops); ++ devnode->cdev.owner = owner; + +- ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); ++ ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); + if (ret < 0) { + pr_err("%s: cdev_add failed\n", __func__); + goto error; + } + + /* Part 3: Register the media device */ +- mdev->dev.bus = &media_bus_type; +- mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); +- mdev->dev.release = media_devnode_release; +- if (mdev->parent) +- mdev->dev.parent = mdev->parent; +- dev_set_name(&mdev->dev, "media%d", mdev->minor); +- ret = device_register(&mdev->dev); ++ devnode->dev.bus = &media_bus_type; ++ devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); ++ devnode->dev.release = media_devnode_release; ++ if (devnode->parent) ++ devnode->dev.parent = devnode->parent; ++ dev_set_name(&devnode->dev, "media%d", devnode->minor); ++ ret = device_register(&devnode->dev); + if (ret < 0) { + pr_err("%s: device_register failed\n", __func__); + goto error; + } + + /* Part 4: Activate this minor. The char device can now be used. */ +- set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + + return 0; + + error: + mutex_lock(&media_devnode_lock); +- cdev_del(&mdev->cdev); +- clear_bit(mdev->minor, media_devnode_nums); ++ cdev_del(&devnode->cdev); ++ clear_bit(devnode->minor, media_devnode_nums); + mutex_unlock(&media_devnode_lock); + + return ret; +@@ -292,7 +292,7 @@ error: + + /** + * media_devnode_unregister - unregister a media device node +- * @mdev: the device node to unregister ++ * @devnode: the device node to unregister + * + * This unregisters the passed device. Future open calls will be met with + * errors. +@@ -300,16 +300,16 @@ error: + * This function can safely be called if the device node has never been + * registered or has already been unregistered. + */ +-void media_devnode_unregister(struct media_devnode *mdev) ++void media_devnode_unregister(struct media_devnode *devnode) + { +- /* Check if mdev was ever registered at all */ +- if (!media_devnode_is_registered(mdev)) ++ /* Check if devnode was ever registered at all */ ++ if (!media_devnode_is_registered(devnode)) + return; + + mutex_lock(&media_devnode_lock); +- clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + mutex_unlock(&media_devnode_lock); +- device_unregister(&mdev->dev); ++ device_unregister(&devnode->dev); + } + + /* +--- a/include/media/media-devnode.h ++++ b/include/media/media-devnode.h +@@ -76,24 +76,24 @@ struct media_devnode { + unsigned long flags; /* Use bitops to access flags */ + + /* callbacks */ +- void (*release)(struct media_devnode *mdev); ++ void (*release)(struct media_devnode *devnode); + }; + + /* dev to media_devnode */ + #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) + +-int __must_check media_devnode_register(struct media_devnode *mdev, ++int __must_check media_devnode_register(struct media_devnode *devnode, + struct module *owner); +-void media_devnode_unregister(struct media_devnode *mdev); ++void media_devnode_unregister(struct media_devnode *devnode); + + static inline struct media_devnode *media_devnode_data(struct file *filp) + { + return filp->private_data; + } + +-static inline int media_devnode_is_registered(struct media_devnode *mdev) ++static inline int media_devnode_is_registered(struct media_devnode *devnode) + { +- return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + } + + #endif /* _MEDIA_DEVNODE_H */ diff --git a/queue-3.16/media-devnode-just-return-0-instead-of-using-a-var.patch b/queue-3.16/media-devnode-just-return-0-instead-of-using-a-var.patch new file mode 100644 index 00000000..9285850f --- /dev/null +++ b/queue-3.16/media-devnode-just-return-0-instead-of-using-a-var.patch @@ -0,0 +1,34 @@ +From: Mauro Carvalho Chehab <m.chehab@samsung.com> +Date: Wed, 3 Sep 2014 15:18:27 -0300 +Subject: [media] media-devnode: just return 0 instead of using a var + +commit 8b37c6455fc8f43e0e95db2847284e618db6a4f8 upstream. + +Instead of allocating a var to store 0 and just return it, +change the code to return 0 directly. + +Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/media/media-devnode.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -192,7 +192,6 @@ static int media_open(struct inode *inod + static int media_release(struct inode *inode, struct file *filp) + { + struct media_devnode *mdev = media_devnode_data(filp); +- int ret = 0; + + if (mdev->fops->release) + mdev->fops->release(filp); +@@ -201,7 +200,7 @@ static int media_release(struct inode *i + return value is ignored. */ + put_device(&mdev->dev); + filp->private_data = NULL; +- return ret; ++ return 0; + } + + static const struct file_operations media_devnode_fops = { diff --git a/queue-3.16/media-fix-media-devnode-ioctl-syscall-and-unregister-race.patch b/queue-3.16/media-fix-media-devnode-ioctl-syscall-and-unregister-race.patch new file mode 100644 index 00000000..197943a9 --- /dev/null +++ b/queue-3.16/media-fix-media-devnode-ioctl-syscall-and-unregister-race.patch @@ -0,0 +1,168 @@ +From: Shuah Khan <shuahkh@osg.samsung.com> +Date: Fri, 10 Jun 2016 14:37:23 -0300 +Subject: [media] media: fix media devnode ioctl/syscall and unregister race + +commit 6f0dd24a084a17f9984dd49dffbf7055bf123993 upstream. + +Media devnode open/ioctl could be in progress when media device unregister +is initiated. System calls and ioctls check media device registered status +at the beginning, however, there is a window where unregister could be in +progress without changing the media devnode status to unregistered. + +process 1 process 2 +fd = open(/dev/media0) +media_devnode_is_registered() + (returns true here) + + media_device_unregister() + (unregister is in progress + and devnode isn't + unregistered yet) + ... +ioctl(fd, ...) +__media_ioctl() +media_devnode_is_registered() + (returns true here) + ... + media_devnode_unregister() + ... + (driver releases the media device + memory) + +media_device_ioctl() + (By this point + devnode->media_dev does not + point to allocated memory. + use-after free in in mutex_lock_nested) + +BUG: KASAN: use-after-free in mutex_lock_nested+0x79c/0x800 at addr +ffff8801ebe914f0 + +Fix it by clearing register bit when unregister starts to avoid the race. + +process 1 process 2 +fd = open(/dev/media0) +media_devnode_is_registered() + (could return true here) + + media_device_unregister() + (clear the register bit, + then start unregister.) + ... +ioctl(fd, ...) +__media_ioctl() +media_devnode_is_registered() + (return false here, ioctl + returns I/O error, and + will not access media + device memory) + ... + media_devnode_unregister() + ... + (driver releases the media device + memory) + +Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> +Suggested-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Reported-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Tested-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> +[bwh: Backported to 3.16: adjut filename, context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -407,6 +407,7 @@ int __must_check __media_device_register + if (ret < 0) { + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; ++ media_devnode_unregister_prepare(devnode); + media_devnode_unregister(devnode); + return ret; + } +@@ -425,16 +426,16 @@ void media_device_unregister(struct medi + struct media_entity *entity; + struct media_entity *next; + ++ /* Clear the devnode register bit to avoid races with media dev open */ ++ media_devnode_unregister_prepare(mdev->devnode); ++ + list_for_each_entry_safe(entity, next, &mdev->entities, list) + media_device_unregister_entity(entity); + +- /* Check if mdev devnode was registered */ +- if (media_devnode_is_registered(mdev->devnode)) { +- device_remove_file(&mdev->devnode->dev, &dev_attr_model); +- media_devnode_unregister(mdev->devnode); +- /* devnode free is handled in media_devnode_*() */ +- mdev->devnode = NULL; +- } ++ device_remove_file(&mdev->devnode->dev, &dev_attr_model); ++ media_devnode_unregister(mdev->devnode); ++ /* devnode free is handled in media_devnode_*() */ ++ mdev->devnode = NULL; + } + EXPORT_SYMBOL_GPL(media_device_unregister); + +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -302,6 +302,17 @@ cdev_add_error: + return ret; + } + ++void media_devnode_unregister_prepare(struct media_devnode *devnode) ++{ ++ /* Check if devnode was ever registered at all */ ++ if (!media_devnode_is_registered(devnode)) ++ return; ++ ++ mutex_lock(&media_devnode_lock); ++ clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); ++ mutex_unlock(&media_devnode_lock); ++} ++ + /** + * media_devnode_unregister - unregister a media device node + * @devnode: the device node to unregister +@@ -309,17 +320,11 @@ cdev_add_error: + * This unregisters the passed device. Future open calls will be met with + * errors. + * +- * This function can safely be called if the device node has never been +- * registered or has already been unregistered. ++ * Should be called after media_devnode_unregister_prepare() + */ + void media_devnode_unregister(struct media_devnode *devnode) + { +- /* Check if devnode was ever registered at all */ +- if (!media_devnode_is_registered(devnode)) +- return; +- + mutex_lock(&media_devnode_lock); +- clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + /* Delete the cdev on this minor as well */ + cdev_del(&devnode->cdev); + mutex_unlock(&media_devnode_lock); +--- a/include/media/media-devnode.h ++++ b/include/media/media-devnode.h +@@ -89,6 +89,20 @@ struct media_devnode { + int __must_check media_devnode_register(struct media_device *mdev, + struct media_devnode *devnode, + struct module *owner); ++ ++/** ++ * media_devnode_unregister_prepare - clear the media device node register bit ++ * @devnode: the device node to prepare for unregister ++ * ++ * This clears the passed device register bit. Future open calls will be met ++ * with errors. Should be called before media_devnode_unregister() to avoid ++ * races with unregister and device file open calls. ++ * ++ * This function can safely be called if the device node has never been ++ * registered or has already been unregistered. ++ */ ++void media_devnode_unregister_prepare(struct media_devnode *devnode); ++ + void media_devnode_unregister(struct media_devnode *devnode); + + static inline struct media_devnode *media_devnode_data(struct file *filp) diff --git a/queue-3.16/media-fix-media_open-to-clear-filp-private_data-in-error.patch b/queue-3.16/media-fix-media_open-to-clear-filp-private_data-in-error.patch new file mode 100644 index 00000000..4be93d5c --- /dev/null +++ b/queue-3.16/media-fix-media_open-to-clear-filp-private_data-in-error.patch @@ -0,0 +1,28 @@ +From: Shuah Khan <shuahkh@osg.samsung.com> +Date: Wed, 27 Jan 2016 21:49:33 -0200 +Subject: [media] media: Fix media_open() to clear filp->private_data in error + leg + +commit d40ec6fdb0b03b7be4c7923a3da0e46bf943740a upstream. + +Fix media_open() to clear filp->private_data when file open +fails. + +Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> +Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/media/media-devnode.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -181,6 +181,7 @@ static int media_open(struct inode *inod + ret = mdev->fops->open(filp); + if (ret) { + put_device(&mdev->dev); ++ filp->private_data = NULL; + return ret; + } + } diff --git a/queue-3.16/media-fix-use-after-free-in-cdev_put-when-app-exits-after.patch b/queue-3.16/media-fix-use-after-free-in-cdev_put-when-app-exits-after.patch new file mode 100644 index 00000000..32a81312 --- /dev/null +++ b/queue-3.16/media-fix-use-after-free-in-cdev_put-when-app-exits-after.patch @@ -0,0 +1,207 @@ +From: Shuah Khan <shuahkh@osg.samsung.com> +Date: Wed, 4 May 2016 16:48:28 -0300 +Subject: [media] media: fix use-after-free in cdev_put() when app exits after + driver unbind + +commit 5b28dde51d0ccc54cee70756e1800d70bed7114a upstream. + +When driver unbinds while media_ioctl is in progress, cdev_put() fails with +when app exits after driver unbinds. + +Add devnode struct device kobj as the cdev parent kobject. cdev_add() gets +a reference to it and releases it in cdev_del() ensuring that the devnode +is not deallocated as long as the application has the device file open. + +media_devnode_register() initializes the struct device kobj before calling +cdev_add(). media_devnode_unregister() does cdev_del() and then deletes the +device. devnode is released when the last reference to the struct device is +gone. + +This problem is found on uvcvideo, em28xx, and au0828 drivers and fix has +been tested on all three. + +kernel: [ 193.599736] BUG: KASAN: use-after-free in cdev_put+0x4e/0x50 +kernel: [ 193.599745] Read of size 8 by task media_device_te/1851 +kernel: [ 193.599792] INFO: Allocated in __media_device_register+0x54 +kernel: [ 193.599951] INFO: Freed in media_devnode_release+0xa4/0xc0 + +kernel: [ 193.601083] Call Trace: +kernel: [ 193.601093] [<ffffffff81aecac3>] dump_stack+0x67/0x94 +kernel: [ 193.601102] [<ffffffff815359b2>] print_trailer+0x112/0x1a0 +kernel: [ 193.601111] [<ffffffff8153b5e4>] object_err+0x34/0x40 +kernel: [ 193.601119] [<ffffffff8153d9d4>] kasan_report_error+0x224/0x530 +kernel: [ 193.601128] [<ffffffff814a2c3d>] ? kzfree+0x2d/0x40 +kernel: [ 193.601137] [<ffffffff81539d72>] ? kfree+0x1d2/0x1f0 +kernel: [ 193.601154] [<ffffffff8157ca7e>] ? cdev_put+0x4e/0x50 +kernel: [ 193.601162] [<ffffffff8157ca7e>] cdev_put+0x4e/0x50 +kernel: [ 193.601170] [<ffffffff815767eb>] __fput+0x52b/0x6c0 +kernel: [ 193.601179] [<ffffffff8117743a>] ? switch_task_namespaces+0x2a +kernel: [ 193.601188] [<ffffffff815769ee>] ____fput+0xe/0x10 +kernel: [ 193.601196] [<ffffffff81170023>] task_work_run+0x133/0x1f0 +kernel: [ 193.601204] [<ffffffff8117746e>] ? switch_task_namespaces+0x5e +kernel: [ 193.601213] [<ffffffff8111b50c>] do_exit+0x72c/0x2c20 +kernel: [ 193.601224] [<ffffffff8111ade0>] ? release_task+0x1250/0x1250 +- +- +- +kernel: [ 193.601360] [<ffffffff81003587>] ? exit_to_usermode_loop+0xe7 +kernel: [ 193.601368] [<ffffffff810035c0>] exit_to_usermode_loop+0x120 +kernel: [ 193.601376] [<ffffffff810061da>] syscall_return_slowpath+0x16a +kernel: [ 193.601386] [<ffffffff82848b33>] entry_SYSCALL_64_fastpath+0xa6 + +Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> +Tested-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> +[bwh: Backported to 3.16: adjust context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/media/media-device.c | 6 +++-- + drivers/media/media-devnode.c | 48 +++++++++++++++++++++-------------- + 2 files changed, 33 insertions(+), 21 deletions(-) + +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -398,16 +398,16 @@ int __must_check __media_device_register + devnode->release = media_device_release; + ret = media_devnode_register(mdev, devnode, owner); + if (ret < 0) { ++ /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; +- kfree(devnode); + return ret; + } + + ret = device_create_file(&devnode->dev, &dev_attr_model); + if (ret < 0) { ++ /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; + media_devnode_unregister(devnode); +- kfree(devnode); + return ret; + } + +@@ -432,6 +432,8 @@ void media_device_unregister(struct medi + if (media_devnode_is_registered(mdev->devnode)) { + device_remove_file(&mdev->devnode->dev, &dev_attr_model); + media_devnode_unregister(mdev->devnode); ++ /* devnode free is handled in media_devnode_*() */ ++ mdev->devnode = NULL; + } + } + EXPORT_SYMBOL_GPL(media_device_unregister); +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -63,13 +63,8 @@ static void media_devnode_release(struct + struct media_devnode *devnode = to_media_devnode(cd); + + mutex_lock(&media_devnode_lock); +- +- /* Delete the cdev on this minor as well */ +- cdev_del(&devnode->cdev); +- + /* Mark device node number as free */ + clear_bit(devnode->minor, media_devnode_nums); +- + mutex_unlock(&media_devnode_lock); + + /* Release media_devnode and perform other cleanups as needed. */ +@@ -77,6 +72,7 @@ static void media_devnode_release(struct + devnode->release(devnode); + + kfree(devnode); ++ pr_debug("%s: Media Devnode Deallocated\n", __func__); + } + + static struct bus_type media_bus_type = { +@@ -205,6 +201,8 @@ static int media_release(struct inode *i + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + put_device(&devnode->dev); ++ ++ pr_debug("%s: Media Release\n", __func__); + return 0; + } + +@@ -250,6 +248,7 @@ int __must_check media_devnode_register( + if (minor == MEDIA_NUM_DEVICES) { + mutex_unlock(&media_devnode_lock); + pr_err("could not get a free minor\n"); ++ kfree(devnode); + return -ENFILE; + } + +@@ -259,27 +258,31 @@ int __must_check media_devnode_register( + devnode->minor = minor; + devnode->media_dev = mdev; + ++ /* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */ ++ devnode->dev.bus = &media_bus_type; ++ devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); ++ devnode->dev.release = media_devnode_release; ++ if (devnode->parent) ++ devnode->dev.parent = devnode->parent; ++ dev_set_name(&devnode->dev, "media%d", devnode->minor); ++ device_initialize(&devnode->dev); ++ + /* Part 2: Initialize and register the character device */ + cdev_init(&devnode->cdev, &media_devnode_fops); + devnode->cdev.owner = owner; ++ devnode->cdev.kobj.parent = &devnode->dev.kobj; + + ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); + if (ret < 0) { + pr_err("%s: cdev_add failed\n", __func__); +- goto error; ++ goto cdev_add_error; + } + +- /* Part 3: Register the media device */ +- devnode->dev.bus = &media_bus_type; +- devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); +- devnode->dev.release = media_devnode_release; +- if (devnode->parent) +- devnode->dev.parent = devnode->parent; +- dev_set_name(&devnode->dev, "media%d", devnode->minor); +- ret = device_register(&devnode->dev); ++ /* Part 3: Add the media device */ ++ ret = device_add(&devnode->dev); + if (ret < 0) { +- pr_err("%s: device_register failed\n", __func__); +- goto error; ++ pr_err("%s: device_add failed\n", __func__); ++ goto device_add_error; + } + + /* Part 4: Activate this minor. The char device can now be used. */ +@@ -287,12 +290,15 @@ int __must_check media_devnode_register( + + return 0; + +-error: +- mutex_lock(&media_devnode_lock); ++device_add_error: + cdev_del(&devnode->cdev); ++cdev_add_error: ++ mutex_lock(&media_devnode_lock); + clear_bit(devnode->minor, media_devnode_nums); ++ devnode->media_dev = NULL; + mutex_unlock(&media_devnode_lock); + ++ put_device(&devnode->dev); + return ret; + } + +@@ -314,8 +320,12 @@ void media_devnode_unregister(struct med + + mutex_lock(&media_devnode_lock); + clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); ++ /* Delete the cdev on this minor as well */ ++ cdev_del(&devnode->cdev); + mutex_unlock(&media_devnode_lock); +- device_unregister(&devnode->dev); ++ device_del(&devnode->dev); ++ devnode->media_dev = NULL; ++ put_device(&devnode->dev); + } + + /* diff --git a/queue-3.16/ptp-create-pins-together-with-the-rest-of-attributes.patch b/queue-3.16/ptp-create-pins-together-with-the-rest-of-attributes.patch new file mode 100644 index 00000000..225ca16f --- /dev/null +++ b/queue-3.16/ptp-create-pins-together-with-the-rest-of-attributes.patch @@ -0,0 +1,158 @@ +From: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Date: Tue, 14 Feb 2017 10:23:34 -0800 +Subject: ptp: create "pins" together with the rest of attributes + +commit 85a66e55019583da1e0f18706b7a8281c9f6de5b upstream. + +Let's switch to using device_create_with_groups(), which will allow us to +create "pins" attribute group together with the rest of ptp device +attributes, and before userspace gets notified about ptp device creation. + +Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +[bwh: Backported to 3.16: adjust context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/ptp/ptp_clock.c | 20 +++++++++++--------- + drivers/ptp/ptp_private.h | 7 ++++--- + drivers/ptp/ptp_sysfs.c | 39 +++++++++------------------------------ + 3 files changed, 24 insertions(+), 42 deletions(-) + +--- a/drivers/ptp/ptp_clock.c ++++ b/drivers/ptp/ptp_clock.c +@@ -210,16 +210,17 @@ struct ptp_clock *ptp_clock_register(str + mutex_init(&ptp->pincfg_mux); + init_waitqueue_head(&ptp->tsev_wq); + ++ err = ptp_populate_pin_groups(ptp); ++ if (err) ++ goto no_pin_groups; ++ + /* Create a new device in our class. */ +- ptp->dev = device_create(ptp_class, parent, ptp->devid, ptp, +- "ptp%d", ptp->index); ++ ptp->dev = device_create_with_groups(ptp_class, parent, ptp->devid, ++ ptp, ptp->pin_attr_groups, ++ "ptp%d", ptp->index); + if (IS_ERR(ptp->dev)) + goto no_device; + +- err = ptp_populate_sysfs(ptp); +- if (err) +- goto no_sysfs; +- + /* Register a new PPS source. */ + if (info->pps) { + struct pps_source_info pps; +@@ -247,10 +248,10 @@ no_clock: + if (ptp->pps_source) + pps_unregister_source(ptp->pps_source); + no_pps: +- ptp_cleanup_sysfs(ptp); +-no_sysfs: + device_destroy(ptp_class, ptp->devid); + no_device: ++ ptp_cleanup_pin_groups(ptp); ++no_pin_groups: + mutex_destroy(&ptp->tsevq_mux); + mutex_destroy(&ptp->pincfg_mux); + no_slot: +@@ -268,8 +269,9 @@ int ptp_clock_unregister(struct ptp_cloc + /* Release the clock's resources. */ + if (ptp->pps_source) + pps_unregister_source(ptp->pps_source); +- ptp_cleanup_sysfs(ptp); ++ + device_destroy(ptp_class, ptp->devid); ++ ptp_cleanup_pin_groups(ptp); + + posix_clock_unregister(&ptp->clock); + return 0; +--- a/drivers/ptp/ptp_private.h ++++ b/drivers/ptp/ptp_private.h +@@ -54,6 +54,8 @@ struct ptp_clock { + struct device_attribute *pin_dev_attr; + struct attribute **pin_attr; + struct attribute_group pin_attr_group; ++ /* 1st entry is a pointer to the real group, 2nd is NULL terminator */ ++ const struct attribute_group *pin_attr_groups[2]; + }; + + /* +@@ -94,8 +96,7 @@ uint ptp_poll(struct posix_clock *pc, + + extern const struct attribute_group *ptp_groups[]; + +-int ptp_cleanup_sysfs(struct ptp_clock *ptp); +- +-int ptp_populate_sysfs(struct ptp_clock *ptp); ++int ptp_populate_pin_groups(struct ptp_clock *ptp); ++void ptp_cleanup_pin_groups(struct ptp_clock *ptp); + + #endif +--- a/drivers/ptp/ptp_sysfs.c ++++ b/drivers/ptp/ptp_sysfs.c +@@ -268,25 +268,14 @@ static ssize_t ptp_pin_store(struct devi + return count; + } + +-int ptp_cleanup_sysfs(struct ptp_clock *ptp) ++int ptp_populate_pin_groups(struct ptp_clock *ptp) + { +- struct device *dev = ptp->dev; +- struct ptp_clock_info *info = ptp->info; +- +- if (info->n_pins) { +- sysfs_remove_group(&dev->kobj, &ptp->pin_attr_group); +- kfree(ptp->pin_attr); +- kfree(ptp->pin_dev_attr); +- } +- return 0; +-} +- +-static int ptp_populate_pins(struct ptp_clock *ptp) +-{ +- struct device *dev = ptp->dev; + struct ptp_clock_info *info = ptp->info; + int err = -ENOMEM, i, n_pins = info->n_pins; + ++ if (!n_pins) ++ return 0; ++ + ptp->pin_dev_attr = kzalloc(n_pins * sizeof(*ptp->pin_dev_attr), + GFP_KERNEL); + if (!ptp->pin_dev_attr) +@@ -310,28 +299,18 @@ static int ptp_populate_pins(struct ptp_ + ptp->pin_attr_group.name = "pins"; + ptp->pin_attr_group.attrs = ptp->pin_attr; + +- err = sysfs_create_group(&dev->kobj, &ptp->pin_attr_group); +- if (err) +- goto no_group; ++ ptp->pin_attr_groups[0] = &ptp->pin_attr_group; ++ + return 0; + +-no_group: +- kfree(ptp->pin_attr); + no_pin_attr: + kfree(ptp->pin_dev_attr); + no_dev_attr: + return err; + } + +-int ptp_populate_sysfs(struct ptp_clock *ptp) ++void ptp_cleanup_pin_groups(struct ptp_clock *ptp) + { +- struct ptp_clock_info *info = ptp->info; +- int err; +- +- if (info->n_pins) { +- err = ptp_populate_pins(ptp); +- if (err) +- return err; +- } +- return 0; ++ kfree(ptp->pin_attr); ++ kfree(ptp->pin_dev_attr); + } diff --git a/queue-3.16/ptp-do-not-explicitly-set-drvdata-in-ptp_clock_register.patch b/queue-3.16/ptp-do-not-explicitly-set-drvdata-in-ptp_clock_register.patch new file mode 100644 index 00000000..9df60aaa --- /dev/null +++ b/queue-3.16/ptp-do-not-explicitly-set-drvdata-in-ptp_clock_register.patch @@ -0,0 +1,28 @@ +From: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Date: Tue, 14 Feb 2017 10:23:31 -0800 +Subject: ptp: do not explicitly set drvdata in ptp_clock_register() + +commit 882f312dc0751c973db26478f07f082c584d16aa upstream. + +We do not need explicitly call dev_set_drvdata(), as it is done for us by +device_create(). + +Acked-by: Richard Cochran <richardcochran@gmail.com> +Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/ptp/ptp_clock.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/ptp/ptp_clock.c ++++ b/drivers/ptp/ptp_clock.c +@@ -216,8 +216,6 @@ struct ptp_clock *ptp_clock_register(str + if (IS_ERR(ptp->dev)) + goto no_device; + +- dev_set_drvdata(ptp->dev, ptp); +- + err = ptp_populate_sysfs(ptp); + if (err) + goto no_sysfs; diff --git a/queue-3.16/ptp-fix-pass-zero-to-err_ptr-in-ptp_clock_register.patch b/queue-3.16/ptp-fix-pass-zero-to-err_ptr-in-ptp_clock_register.patch new file mode 100644 index 00000000..69fd5d85 --- /dev/null +++ b/queue-3.16/ptp-fix-pass-zero-to-err_ptr-in-ptp_clock_register.patch @@ -0,0 +1,45 @@ +From: YueHaibing <yuehaibing@huawei.com> +Date: Fri, 23 Nov 2018 09:54:55 +0800 +Subject: ptp: Fix pass zero to ERR_PTR() in ptp_clock_register + +commit aea0a897af9e44c258e8ab9296fad417f1bc063a upstream. + +Fix smatch warning: + +drivers/ptp/ptp_clock.c:298 ptp_clock_register() warn: + passing zero to 'ERR_PTR' + +'err' should be set while device_create_with_groups and +pps_register_source fails + +Fixes: 85a66e550195 ("ptp: create "pins" together with the rest of attributes") +Signed-off-by: YueHaibing <yuehaibing@huawei.com> +Acked-by: Richard Cochran <richardcochran@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/ptp/ptp_clock.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/ptp/ptp_clock.c ++++ b/drivers/ptp/ptp_clock.c +@@ -218,8 +218,10 @@ struct ptp_clock *ptp_clock_register(str + ptp->dev = device_create_with_groups(ptp_class, parent, ptp->devid, + ptp, ptp->pin_attr_groups, + "ptp%d", ptp->index); +- if (IS_ERR(ptp->dev)) ++ if (IS_ERR(ptp->dev)) { ++ err = PTR_ERR(ptp->dev); + goto no_device; ++ } + + /* Register a new PPS source. */ + if (info->pps) { +@@ -230,6 +232,7 @@ struct ptp_clock *ptp_clock_register(str + pps.owner = info->owner; + ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS); + if (!ptp->pps_source) { ++ err = -EINVAL; + pr_err("failed to register pps source\n"); + goto no_pps; + } diff --git a/queue-3.16/ptp-fix-the-race-between-the-release-of-ptp_clock-and-cdev.patch b/queue-3.16/ptp-fix-the-race-between-the-release-of-ptp_clock-and-cdev.patch new file mode 100644 index 00000000..8090a6f5 --- /dev/null +++ b/queue-3.16/ptp-fix-the-race-between-the-release-of-ptp_clock-and-cdev.patch @@ -0,0 +1,314 @@ +From: Vladis Dronov <vdronov@redhat.com> +Date: Fri, 27 Dec 2019 03:26:27 +0100 +Subject: ptp: fix the race between the release of ptp_clock and cdev + +commit a33121e5487b424339636b25c35d3a180eaa5f5e upstream. + +In a case when a ptp chardev (like /dev/ptp0) is open but an underlying +device is removed, closing this file leads to a race. This reproduces +easily in a kvm virtual machine: + +ts# cat openptp0.c +int main() { ... fp = fopen("/dev/ptp0", "r"); ... sleep(10); } +ts# uname -r +5.5.0-rc3-46cf053e +ts# cat /proc/cmdline +... slub_debug=FZP +ts# modprobe ptp_kvm +ts# ./openptp0 & +[1] 670 +opened /dev/ptp0, sleeping 10s... +ts# rmmod ptp_kvm +ts# ls /dev/ptp* +ls: cannot access '/dev/ptp*': No such file or directory +ts# ...woken up +[ 48.010809] general protection fault: 0000 [#1] SMP +[ 48.012502] CPU: 6 PID: 658 Comm: openptp0 Not tainted 5.5.0-rc3-46cf053e #25 +[ 48.014624] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), ... +[ 48.016270] RIP: 0010:module_put.part.0+0x7/0x80 +[ 48.017939] RSP: 0018:ffffb3850073be00 EFLAGS: 00010202 +[ 48.018339] RAX: 000000006b6b6b6b RBX: 6b6b6b6b6b6b6b6b RCX: ffff89a476c00ad0 +[ 48.018936] RDX: fffff65a08d3ea08 RSI: 0000000000000247 RDI: 6b6b6b6b6b6b6b6b +[ 48.019470] ... ^^^ a slub poison +[ 48.023854] Call Trace: +[ 48.024050] __fput+0x21f/0x240 +[ 48.024288] task_work_run+0x79/0x90 +[ 48.024555] do_exit+0x2af/0xab0 +[ 48.024799] ? vfs_write+0x16a/0x190 +[ 48.025082] do_group_exit+0x35/0x90 +[ 48.025387] __x64_sys_exit_group+0xf/0x10 +[ 48.025737] do_syscall_64+0x3d/0x130 +[ 48.026056] entry_SYSCALL_64_after_hwframe+0x44/0xa9 +[ 48.026479] RIP: 0033:0x7f53b12082f6 +[ 48.026792] ... +[ 48.030945] Modules linked in: ptp i6300esb watchdog [last unloaded: ptp_kvm] +[ 48.045001] Fixing recursive fault but reboot is needed! + +This happens in: + +static void __fput(struct file *file) +{ ... + if (file->f_op->release) + file->f_op->release(inode, file); <<< cdev is kfree'd here + if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL && + !(mode & FMODE_PATH))) { + cdev_put(inode->i_cdev); <<< cdev fields are accessed here + +Namely: + +__fput() + posix_clock_release() + kref_put(&clk->kref, delete_clock) <<< the last reference + delete_clock() + delete_ptp_clock() + kfree(ptp) <<< cdev is embedded in ptp + cdev_put + module_put(p->owner) <<< *p is kfree'd, bang! + +Here cdev is embedded in posix_clock which is embedded in ptp_clock. +The race happens because ptp_clock's lifetime is controlled by two +refcounts: kref and cdev.kobj in posix_clock. This is wrong. + +Make ptp_clock's sysfs device a parent of cdev with cdev_device_add() +created especially for such cases. This way the parent device with its +ptp_clock is not released until all references to the cdev are released. +This adds a requirement that an initialized but not exposed struct +device should be provided to posix_clock_register() by a caller instead +of a simple dev_t. + +This approach was adopted from the commit 72139dfa2464 ("watchdog: Fix +the race between the release of watchdog_core_data and cdev"). See +details of the implementation in the commit 233ed09d7fda ("chardev: add +helper function to register char devs with a struct device"). + +Link: https://lore.kernel.org/linux-fsdevel/20191125125342.6189-1-vdronov@redhat.com/T/#u +Analyzed-by: Stephen Johnston <sjohnsto@redhat.com> +Analyzed-by: Vern Lovejoy <vlovejoy@redhat.com> +Signed-off-by: Vladis Dronov <vdronov@redhat.com> +Acked-by: Richard Cochran <richardcochran@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/ptp/ptp_clock.c | 31 ++++++++++++++----------------- + drivers/ptp/ptp_private.h | 2 +- + include/linux/posix-clock.h | 19 +++++++++++-------- + kernel/time/posix-clock.c | 31 +++++++++++++------------------ + 4 files changed, 39 insertions(+), 44 deletions(-) + +--- a/drivers/ptp/ptp_clock.c ++++ b/drivers/ptp/ptp_clock.c +@@ -167,9 +167,9 @@ static struct posix_clock_operations ptp + .read = ptp_read, + }; + +-static void delete_ptp_clock(struct posix_clock *pc) ++static void ptp_clock_release(struct device *dev) + { +- struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); ++ struct ptp_clock *ptp = container_of(dev, struct ptp_clock, dev); + + mutex_destroy(&ptp->tsevq_mux); + mutex_destroy(&ptp->pincfg_mux); +@@ -201,7 +201,6 @@ struct ptp_clock *ptp_clock_register(str + } + + ptp->clock.ops = ptp_clock_ops; +- ptp->clock.release = delete_ptp_clock; + ptp->info = info; + ptp->devid = MKDEV(major, index); + ptp->index = index; +@@ -214,15 +213,6 @@ struct ptp_clock *ptp_clock_register(str + if (err) + goto no_pin_groups; + +- /* Create a new device in our class. */ +- ptp->dev = device_create_with_groups(ptp_class, parent, ptp->devid, +- ptp, ptp->pin_attr_groups, +- "ptp%d", ptp->index); +- if (IS_ERR(ptp->dev)) { +- err = PTR_ERR(ptp->dev); +- goto no_device; +- } +- + /* Register a new PPS source. */ + if (info->pps) { + struct pps_source_info pps; +@@ -238,8 +228,18 @@ struct ptp_clock *ptp_clock_register(str + } + } + +- /* Create a posix clock. */ +- err = posix_clock_register(&ptp->clock, ptp->devid); ++ /* Initialize a new device of our class in our clock structure. */ ++ device_initialize(&ptp->dev); ++ ptp->dev.devt = ptp->devid; ++ ptp->dev.class = ptp_class; ++ ptp->dev.parent = parent; ++ ptp->dev.groups = ptp->pin_attr_groups; ++ ptp->dev.release = ptp_clock_release; ++ dev_set_drvdata(&ptp->dev, ptp); ++ dev_set_name(&ptp->dev, "ptp%d", ptp->index); ++ ++ /* Create a posix clock and link it to the device. */ ++ err = posix_clock_register(&ptp->clock, &ptp->dev); + if (err) { + pr_err("failed to create posix clock\n"); + goto no_clock; +@@ -251,8 +251,6 @@ no_clock: + if (ptp->pps_source) + pps_unregister_source(ptp->pps_source); + no_pps: +- device_destroy(ptp_class, ptp->devid); +-no_device: + ptp_cleanup_pin_groups(ptp); + no_pin_groups: + mutex_destroy(&ptp->tsevq_mux); +@@ -273,7 +271,6 @@ int ptp_clock_unregister(struct ptp_cloc + if (ptp->pps_source) + pps_unregister_source(ptp->pps_source); + +- device_destroy(ptp_class, ptp->devid); + ptp_cleanup_pin_groups(ptp); + + posix_clock_unregister(&ptp->clock); +--- a/drivers/ptp/ptp_private.h ++++ b/drivers/ptp/ptp_private.h +@@ -40,7 +40,7 @@ struct timestamp_event_queue { + + struct ptp_clock { + struct posix_clock clock; +- struct device *dev; ++ struct device dev; + struct ptp_clock_info *info; + dev_t devid; + int index; /* index into clocks.map */ +--- a/include/linux/posix-clock.h ++++ b/include/linux/posix-clock.h +@@ -104,29 +104,32 @@ struct posix_clock_operations { + * + * @ops: Functional interface to the clock + * @cdev: Character device instance for this clock +- * @kref: Reference count. ++ * @dev: Pointer to the clock's device. + * @rwsem: Protects the 'zombie' field from concurrent access. + * @zombie: If 'zombie' is true, then the hardware has disappeared. +- * @release: A function to free the structure when the reference count reaches +- * zero. May be NULL if structure is statically allocated. + * + * Drivers should embed their struct posix_clock within a private + * structure, obtaining a reference to it during callbacks using + * container_of(). ++ * ++ * Drivers should supply an initialized but not exposed struct device ++ * to posix_clock_register(). It is used to manage lifetime of the ++ * driver's private structure. It's 'release' field should be set to ++ * a release function for this private structure. + */ + struct posix_clock { + struct posix_clock_operations ops; + struct cdev cdev; +- struct kref kref; ++ struct device *dev; + struct rw_semaphore rwsem; + bool zombie; +- void (*release)(struct posix_clock *clk); + }; + + /** + * posix_clock_register() - register a new clock +- * @clk: Pointer to the clock. Caller must provide 'ops' and 'release' +- * @devid: Allocated device id ++ * @clk: Pointer to the clock. Caller must provide 'ops' field ++ * @dev: Pointer to the initialized device. Caller must provide ++ * 'release' field + * + * A clock driver calls this function to register itself with the + * clock device subsystem. If 'clk' points to dynamically allocated +@@ -135,7 +138,7 @@ struct posix_clock { + * + * Returns zero on success, non-zero otherwise. + */ +-int posix_clock_register(struct posix_clock *clk, dev_t devid); ++int posix_clock_register(struct posix_clock *clk, struct device *dev); + + /** + * posix_clock_unregister() - unregister a clock +--- a/kernel/time/posix-clock.c ++++ b/kernel/time/posix-clock.c +@@ -25,8 +25,6 @@ + #include <linux/syscalls.h> + #include <linux/uaccess.h> + +-static void delete_clock(struct kref *kref); +- + /* + * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. + */ +@@ -168,7 +166,7 @@ static int posix_clock_open(struct inode + err = 0; + + if (!err) { +- kref_get(&clk->kref); ++ get_device(clk->dev); + fp->private_data = clk; + } + out: +@@ -184,7 +182,7 @@ static int posix_clock_release(struct in + if (clk->ops.release) + err = clk->ops.release(clk); + +- kref_put(&clk->kref, delete_clock); ++ put_device(clk->dev); + + fp->private_data = NULL; + +@@ -206,38 +204,35 @@ static const struct file_operations posi + #endif + }; + +-int posix_clock_register(struct posix_clock *clk, dev_t devid) ++int posix_clock_register(struct posix_clock *clk, struct device *dev) + { + int err; + +- kref_init(&clk->kref); + init_rwsem(&clk->rwsem); + + cdev_init(&clk->cdev, &posix_clock_file_operations); ++ err = cdev_device_add(&clk->cdev, dev); ++ if (err) { ++ pr_err("%s unable to add device %d:%d\n", ++ dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt)); ++ return err; ++ } + clk->cdev.owner = clk->ops.owner; +- err = cdev_add(&clk->cdev, devid, 1); ++ clk->dev = dev; + +- return err; ++ return 0; + } + EXPORT_SYMBOL_GPL(posix_clock_register); + +-static void delete_clock(struct kref *kref) +-{ +- struct posix_clock *clk = container_of(kref, struct posix_clock, kref); +- +- if (clk->release) +- clk->release(clk); +-} +- + void posix_clock_unregister(struct posix_clock *clk) + { +- cdev_del(&clk->cdev); ++ cdev_device_del(&clk->cdev, clk->dev); + + down_write(&clk->rwsem); + clk->zombie = true; + up_write(&clk->rwsem); + +- kref_put(&clk->kref, delete_clock); ++ put_device(clk->dev); + } + EXPORT_SYMBOL_GPL(posix_clock_unregister); + diff --git a/queue-3.16/ptp-free-ptp-device-pin-descriptors-properly.patch b/queue-3.16/ptp-free-ptp-device-pin-descriptors-properly.patch new file mode 100644 index 00000000..ab725d54 --- /dev/null +++ b/queue-3.16/ptp-free-ptp-device-pin-descriptors-properly.patch @@ -0,0 +1,48 @@ +From: Vladis Dronov <vdronov@redhat.com> +Date: Mon, 13 Jan 2020 14:00:09 +0100 +Subject: ptp: free ptp device pin descriptors properly + +commit 75718584cb3c64e6269109d4d54f888ac5a5fd15 upstream. + +There is a bug in ptp_clock_unregister(), where ptp_cleanup_pin_groups() +first frees ptp->pin_{,dev_}attr, but then posix_clock_unregister() needs +them to destroy a related sysfs device. + +These functions can not be just swapped, as posix_clock_unregister() frees +ptp which is needed in the ptp_cleanup_pin_groups(). Fix this by calling +ptp_cleanup_pin_groups() in ptp_clock_release(), right before ptp is freed. + +This makes this patch fix an UAF bug in a patch which fixes an UAF bug. + +Reported-by: Antti Laakso <antti.laakso@intel.com> +Fixes: a33121e5487b ("ptp: fix the race between the release of ptp_clock and cdev") +Link: https://lore.kernel.org/netdev/3d2bd09735dbdaf003585ca376b7c1e5b69a19bd.camel@intel.com/ +Signed-off-by: Vladis Dronov <vdronov@redhat.com> +Acked-by: Richard Cochran <richardcochran@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/ptp/ptp_clock.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/ptp/ptp_clock.c ++++ b/drivers/ptp/ptp_clock.c +@@ -171,6 +171,7 @@ static void ptp_clock_release(struct dev + { + struct ptp_clock *ptp = container_of(dev, struct ptp_clock, dev); + ++ ptp_cleanup_pin_groups(ptp); + mutex_destroy(&ptp->tsevq_mux); + mutex_destroy(&ptp->pincfg_mux); + ida_simple_remove(&ptp_clocks_map, ptp->index); +@@ -271,9 +272,8 @@ int ptp_clock_unregister(struct ptp_cloc + if (ptp->pps_source) + pps_unregister_source(ptp->pps_source); + +- ptp_cleanup_pin_groups(ptp); +- + posix_clock_unregister(&ptp->clock); ++ + return 0; + } + EXPORT_SYMBOL(ptp_clock_unregister); diff --git a/queue-3.16/ptp-use-is_visible-method-to-hide-unused-attributes.patch b/queue-3.16/ptp-use-is_visible-method-to-hide-unused-attributes.patch new file mode 100644 index 00000000..a559264a --- /dev/null +++ b/queue-3.16/ptp-use-is_visible-method-to-hide-unused-attributes.patch @@ -0,0 +1,206 @@ +From: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Date: Tue, 14 Feb 2017 10:23:33 -0800 +Subject: ptp: use is_visible method to hide unused attributes + +commit af59e717d5ff9c8dbf9bcc581c0dfb3b2a9c9030 upstream. + +Instead of creating selected attributes after the device is created (and +after userspace potentially seen uevent), lets use attribute group +is_visible() method to control which attributes are shown. This will allow +us to create all attributes (except "pins" group, which will be taken care +of later) before userspace gets notified about new ptp class device. + +Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/ptp/ptp_sysfs.c | 125 ++++++++++++++++++---------------------- + 1 file changed, 55 insertions(+), 70 deletions(-) + +--- a/drivers/ptp/ptp_sysfs.c ++++ b/drivers/ptp/ptp_sysfs.c +@@ -46,27 +46,6 @@ PTP_SHOW_INT(n_periodic_outputs, n_per_o + PTP_SHOW_INT(n_programmable_pins, n_pins); + PTP_SHOW_INT(pps_available, pps); + +-static struct attribute *ptp_attrs[] = { +- &dev_attr_clock_name.attr, +- &dev_attr_max_adjustment.attr, +- &dev_attr_n_alarms.attr, +- &dev_attr_n_external_timestamps.attr, +- &dev_attr_n_periodic_outputs.attr, +- &dev_attr_n_programmable_pins.attr, +- &dev_attr_pps_available.attr, +- NULL, +-}; +- +-static const struct attribute_group ptp_group = { +- .attrs = ptp_attrs, +-}; +- +-const struct attribute_group *ptp_groups[] = { +- &ptp_group, +- NULL, +-}; +- +- + static ssize_t extts_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +@@ -91,6 +70,7 @@ static ssize_t extts_enable_store(struct + out: + return err; + } ++static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); + + static ssize_t extts_fifo_show(struct device *dev, + struct device_attribute *attr, char *page) +@@ -124,6 +104,7 @@ out: + mutex_unlock(&ptp->tsevq_mux); + return cnt; + } ++static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); + + static ssize_t period_store(struct device *dev, + struct device_attribute *attr, +@@ -151,6 +132,7 @@ static ssize_t period_store(struct devic + out: + return err; + } ++static DEVICE_ATTR(period, 0220, NULL, period_store); + + static ssize_t pps_enable_store(struct device *dev, + struct device_attribute *attr, +@@ -177,6 +159,57 @@ static ssize_t pps_enable_store(struct d + out: + return err; + } ++static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store); ++ ++static struct attribute *ptp_attrs[] = { ++ &dev_attr_clock_name.attr, ++ ++ &dev_attr_max_adjustment.attr, ++ &dev_attr_n_alarms.attr, ++ &dev_attr_n_external_timestamps.attr, ++ &dev_attr_n_periodic_outputs.attr, ++ &dev_attr_n_programmable_pins.attr, ++ &dev_attr_pps_available.attr, ++ ++ &dev_attr_extts_enable.attr, ++ &dev_attr_fifo.attr, ++ &dev_attr_period.attr, ++ &dev_attr_pps_enable.attr, ++ NULL ++}; ++ ++static umode_t ptp_is_attribute_visible(struct kobject *kobj, ++ struct attribute *attr, int n) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct ptp_clock *ptp = dev_get_drvdata(dev); ++ struct ptp_clock_info *info = ptp->info; ++ umode_t mode = attr->mode; ++ ++ if (attr == &dev_attr_extts_enable.attr || ++ attr == &dev_attr_fifo.attr) { ++ if (!info->n_ext_ts) ++ mode = 0; ++ } else if (attr == &dev_attr_period.attr) { ++ if (!info->n_per_out) ++ mode = 0; ++ } else if (attr == &dev_attr_pps_enable.attr) { ++ if (!info->pps) ++ mode = 0; ++ } ++ ++ return mode; ++} ++ ++static const struct attribute_group ptp_group = { ++ .is_visible = ptp_is_attribute_visible, ++ .attrs = ptp_attrs, ++}; ++ ++const struct attribute_group *ptp_groups[] = { ++ &ptp_group, ++ NULL ++}; + + static int ptp_pin_name2index(struct ptp_clock *ptp, const char *name) + { +@@ -235,26 +268,11 @@ static ssize_t ptp_pin_store(struct devi + return count; + } + +-static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); +-static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); +-static DEVICE_ATTR(period, 0220, NULL, period_store); +-static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store); +- + int ptp_cleanup_sysfs(struct ptp_clock *ptp) + { + struct device *dev = ptp->dev; + struct ptp_clock_info *info = ptp->info; + +- if (info->n_ext_ts) { +- device_remove_file(dev, &dev_attr_extts_enable); +- device_remove_file(dev, &dev_attr_fifo); +- } +- if (info->n_per_out) +- device_remove_file(dev, &dev_attr_period); +- +- if (info->pps) +- device_remove_file(dev, &dev_attr_pps_enable); +- + if (info->n_pins) { + sysfs_remove_group(&dev->kobj, &ptp->pin_attr_group); + kfree(ptp->pin_attr); +@@ -307,46 +325,13 @@ no_dev_attr: + + int ptp_populate_sysfs(struct ptp_clock *ptp) + { +- struct device *dev = ptp->dev; + struct ptp_clock_info *info = ptp->info; + int err; + +- if (info->n_ext_ts) { +- err = device_create_file(dev, &dev_attr_extts_enable); +- if (err) +- goto out1; +- err = device_create_file(dev, &dev_attr_fifo); +- if (err) +- goto out2; +- } +- if (info->n_per_out) { +- err = device_create_file(dev, &dev_attr_period); +- if (err) +- goto out3; +- } +- if (info->pps) { +- err = device_create_file(dev, &dev_attr_pps_enable); +- if (err) +- goto out4; +- } + if (info->n_pins) { + err = ptp_populate_pins(ptp); + if (err) +- goto out5; ++ return err; + } + return 0; +-out5: +- if (info->pps) +- device_remove_file(dev, &dev_attr_pps_enable); +-out4: +- if (info->n_per_out) +- device_remove_file(dev, &dev_attr_period); +-out3: +- if (info->n_ext_ts) +- device_remove_file(dev, &dev_attr_fifo); +-out2: +- if (info->n_ext_ts) +- device_remove_file(dev, &dev_attr_extts_enable); +-out1: +- return err; + } diff --git a/queue-3.16/series b/queue-3.16/series index dab52b70..a676e4a8 100644 --- a/queue-3.16/series +++ b/queue-3.16/series @@ -227,3 +227,19 @@ mm-mempolicy-require-at-least-one-nodeid-for-mpol_preferred.patch media-ov519-add-missing-endpoint-sanity-checks.patch media-stv06xx-add-missing-descriptor-sanity-checks.patch media-xirlink_cit-add-missing-descriptor-sanity-checks.patch +ptp-do-not-explicitly-set-drvdata-in-ptp_clock_register.patch +ptp-use-is_visible-method-to-hide-unused-attributes.patch +ptp-create-pins-together-with-the-rest-of-attributes.patch +chardev-add-helper-function-to-register-char-devs-with-a-struct.patch +ptp-fix-pass-zero-to-err_ptr-in-ptp_clock_register.patch +ptp-fix-the-race-between-the-release-of-ptp_clock-and-cdev.patch +ptp-free-ptp-device-pin-descriptors-properly.patch +media-devnode-just-return-0-instead-of-using-a-var.patch +media-fix-media_open-to-clear-filp-private_data-in-error.patch +drivers-media-media-devnode-clear-private_data-before.patch +media-devnode-add-missing-mutex-lock-in-error-handler.patch +media-devnode-fix-namespace-mess.patch +media-device-dynamically-allocate-struct-media_devnode.patch +media-fix-use-after-free-in-cdev_put-when-app-exits-after.patch +media-fix-media-devnode-ioctl-syscall-and-unregister-race.patch +slcan-don-t-transmit-uninitialized-stack-data-in-padding.patch diff --git a/queue-3.16/slcan-don-t-transmit-uninitialized-stack-data-in-padding.patch b/queue-3.16/slcan-don-t-transmit-uninitialized-stack-data-in-padding.patch new file mode 100644 index 00000000..b858b070 --- /dev/null +++ b/queue-3.16/slcan-don-t-transmit-uninitialized-stack-data-in-padding.patch @@ -0,0 +1,48 @@ +From: Richard Palethorpe <rpalethorpe@suse.com> +Date: Wed, 1 Apr 2020 12:06:39 +0200 +Subject: slcan: Don't transmit uninitialized stack data in padding + +commit b9258a2cece4ec1f020715fe3554bc2e360f6264 upstream. + +struct can_frame contains some padding which is not explicitly zeroed in +slc_bump. This uninitialized data will then be transmitted if the stack +initialization hardening feature is not enabled (CONFIG_INIT_STACK_ALL). + +This commit just zeroes the whole struct including the padding. + +Signed-off-by: Richard Palethorpe <rpalethorpe@suse.com> +Fixes: a1044e36e457 ("can: add slcan driver for serial/USB-serial CAN adapters") +Reviewed-by: Kees Cook <keescook@chromium.org> +Cc: linux-can@vger.kernel.org +Cc: netdev@vger.kernel.org +Cc: security@kernel.org +Cc: wg@grandegger.com +Cc: mkl@pengutronix.de +Cc: davem@davemloft.net +Acked-by: Marc Kleine-Budde <mkl@pengutronix.de> +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/net/can/slcan.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/can/slcan.c ++++ b/drivers/net/can/slcan.c +@@ -150,7 +150,7 @@ static void slc_bump(struct slcan *sl) + u32 tmpid; + char *cmd = sl->rbuff; + +- cf.can_id = 0; ++ memset(&cf, 0, sizeof(cf)); + + switch (*cmd) { + case 'r': +@@ -189,8 +189,6 @@ static void slc_bump(struct slcan *sl) + else + return; + +- *(u64 *) (&cf.data) = 0; /* clear payload */ +- + /* RTR frames may have a dlc > 0 but they never have any data bytes */ + if (!(cf.can_id & CAN_RTR_FLAG)) { + for (i = 0; i < cf.can_dlc; i++) { |