Re: [PATCH] [media] media-devnode: Alloc cdev dynamically

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/24/2016 08:59 PM, Mauro Carvalho Chehab wrote:
> Currently, cdev is embedded inside media_devnode. This causes
> a problem with the fs core, as __fput() will try to release
> its access by calling cdev_put():
> 
> [  399.653545] BUG: KASAN: use-after-free in media_release+0xe1/0xf0 [media] at addr ffff88036a9ba4e0
> [  399.653550] Read of size 8 by task mc_nextgen_test/19761
> [  399.653554] page:ffffea000daa6e80 count:0 mapcount:0 mapping:          (null) index:0xffff88036a9bad20
> [  399.653559] flags: 0x2ffff8000000000()
> [  399.653562] page dumped because: kasan: bad access detected
> [  399.653567] CPU: 1 PID: 19761 Comm: mc_nextgen_test Tainted: G    B           4.5.0+ #62
> [  399.653570] Hardware name:                  /NUC5i7RYB, BIOS RYBDWi35.86A.0350.2015.0812.1722 08/12/2015
> [  399.653574]  ffff88036a9ba4e0 ffff8803c465fd10 ffffffff819447c1 ffff88036a9ba4e0
> [  399.653582]  ffff8803c465fda8 ffff8803c465fd98 ffffffff8156ef05 0000000800000001
> [  399.653591]  ffff8803c689fa10 0000000000000292 0000000041b58ab3 ffffffff82813e00
> [  399.653599] Call Trace:
> [  399.653604]  [<ffffffff819447c1>] dump_stack+0x85/0xc4
> [  399.653609]  [<ffffffff8156ef05>] kasan_report_error+0x525/0x550
> [  399.653615]  [<ffffffff81685d10>] ? __fsnotify_inode_delete+0x20/0x20
> [  399.653620]  [<ffffffff8124acd0>] ? debug_check_no_locks_freed+0x290/0x290
> [  399.653626]  [<ffffffff8156f063>] __asan_report_load8_noabort+0x43/0x50
> [  399.653633]  [<ffffffffa11f53b1>] ? media_release+0xe1/0xf0 [media]
> [  399.653640]  [<ffffffffa11f53b1>] media_release+0xe1/0xf0 [media]
> [  399.653646]  [<ffffffff815c2c4f>] __fput+0x20f/0x6d0
> [  399.653651]  [<ffffffff815c317e>] ____fput+0xe/0x10
> [  399.653656]  [<ffffffff811acde7>] task_work_run+0x137/0x200
> [  399.653662]  [<ffffffff81005d54>] exit_to_usermode_loop+0x154/0x180
> [  399.653667]  [<ffffffff8124a1b6>] ? trace_hardirqs_on_caller+0x16/0x590
> [  399.653672]  [<ffffffff810073a6>] syscall_return_slowpath+0x186/0x1c0
> [  399.653678]  [<ffffffff822e7a1c>] entry_SYSCALL_64_fastpath+0xbf/0xc1
> 
> There are two alternatives to solve it: we could either use a static
> var for cdev or to dynamically allocate it. Let's choose the last one,
> as this is the same solution at v4l2 core, from where this code seems
> to have originated.

For reference only: when I posted my CEC framework code it was based on what
v4l2-dev.c did, and I got yelled at by Russell King. I took his advice and
the new approach seems to work well without having to allocate cdev.

The code is here:

http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/100380/focus=100378

Search for cec_devnode_register.

Russell's mail is here:

http://www.spinics.net/lists/dri-devel/msg88417.html

However, the struct cec_adapter itself does have to be allocated, otherwise you
will get nasty lifetime issues. This seems to be a common theme: allocate the
main struct (cec_adapter, video_device, rc_device), then register the character
device as a separate step. On advantage of this is that a driver can allocate
everything first and do the registration of the devices as the last step when
it knows everything is consistent and initialized properly.

We've been embedding video_device/media_device in top-level structs and that looks
like it was a bad idea.

Digging into this mess is time consuming, but I thought I should at least share
this advice from Russell as an example.

Regards,

	Hans

> 
> Signed-off-by: Mauro Carvalho Chehab <mchehab@xxxxxxxxxxxxxxx>
> ---
>  drivers/media/media-devnode.c | 39 ++++++++++++++++++++++++++-------------
>  include/media/media-devnode.h |  4 ++--
>  2 files changed, 28 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
> index db47063d8801..7f9a7e65df20 100644
> --- a/drivers/media/media-devnode.c
> +++ b/drivers/media/media-devnode.c
> @@ -56,6 +56,7 @@ static dev_t media_dev_t;
>   */
>  static DEFINE_MUTEX(media_devnode_lock);
>  static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
> +static struct media_devnode *media_minors[MEDIA_NUM_DEVICES];
>  
>  /* Called when the last user of the media device exits. */
>  static void media_devnode_release(struct device *cd)
> @@ -65,7 +66,9 @@ static void media_devnode_release(struct device *cd)
>  	mutex_lock(&media_devnode_lock);
>  
>  	/* Delete the cdev on this minor as well */
> -	cdev_del(&devnode->cdev);
> +	cdev_del(devnode->cdev);
> +	devnode->cdev = NULL;
> +	media_minors[devnode->minor] = NULL;
>  
>  	/* Mark device node number as free */
>  	clear_bit(devnode->minor, media_devnode_nums);
> @@ -167,9 +170,7 @@ static int media_open(struct inode *inode, struct file *filp)
>  	 * a crash.
>  	 */
>  	mutex_lock(&media_devnode_lock);
> -	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. */
> +	devnode = media_minors[iminor(inode)];
>  	if (!media_devnode_is_registered(devnode)) {
>  		mutex_unlock(&media_devnode_lock);
>  		return -ENXIO;
> @@ -227,6 +228,7 @@ int __must_check media_devnode_register(struct media_device *mdev,
>  {
>  	int minor;
>  	int ret;
> +	dev_t devt;
>  
>  	/* Part 1: Find a free minor number */
>  	mutex_lock(&media_devnode_lock);
> @@ -238,28 +240,35 @@ int __must_check media_devnode_register(struct media_device *mdev,
>  	}
>  
>  	set_bit(minor, media_devnode_nums);
> +	media_minors[minor] = devnode;
>  	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);
> -	devnode->cdev.owner = owner;
> +	devnode->cdev = cdev_alloc();
> +	if (!devnode->cdev) {
> +		ret = -ENOMEM;
> +		goto error;
> +	}
>  
> -	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
> +	cdev_init(devnode->cdev, &media_devnode_fops);
> +	devnode->cdev->owner = owner;
> +
> +	devt = MKDEV(MAJOR(media_dev_t), minor);
> +	ret = cdev_add(devnode->cdev, devt, 1);
>  	if (ret < 0) {
>  		pr_err("%s: cdev_add failed\n", __func__);
>  		goto error;
>  	}
>  
>  	/* Part 3: Register the media device */
> +	devnode->minor = minor;
> +	devnode->media_dev = mdev;
>  	devnode->dev.bus = &media_bus_type;
> -	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
> +	devnode->dev.devt = devt;
>  	devnode->dev.release = media_devnode_release;
>  	if (devnode->parent)
>  		devnode->dev.parent = devnode->parent;
> -	dev_set_name(&devnode->dev, "media%d", devnode->minor);
> +	dev_set_name(&devnode->dev, "media%d", minor);
>  	ret = device_register(&devnode->dev);
>  	if (ret < 0) {
>  		pr_err("%s: device_register failed\n", __func__);
> @@ -273,8 +282,12 @@ int __must_check media_devnode_register(struct media_device *mdev,
>  
>  error:
>  	mutex_lock(&media_devnode_lock);
> -	cdev_del(&devnode->cdev);
> +	if (devnode->cdev) {
> +		cdev_del(devnode->cdev);
> +		devnode->cdev = NULL;
> +	}
>  	clear_bit(devnode->minor, media_devnode_nums);
> +	media_minors[minor] = NULL;
>  	mutex_unlock(&media_devnode_lock);
>  
>  	return ret;
> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> index cc2b3155593c..9fe627ca5ec9 100644
> --- a/include/media/media-devnode.h
> +++ b/include/media/media-devnode.h
> @@ -71,7 +71,7 @@ struct media_file_operations {
>   * struct media_devnode - Media device node
>   * @fops:	pointer to struct &media_file_operations with media device ops
>   * @dev:	struct device pointer for the media controller device
> - * @cdev:	struct cdev pointer character device
> + * @cdev:      struct cdev pointer character device
>   * @parent:	parent device
>   * @minor:	device node minor number
>   * @flags:	flags, combination of the MEDIA_FLAG_* constants
> @@ -90,7 +90,7 @@ struct media_devnode {
>  
>  	/* sysfs */
>  	struct device dev;		/* media device */
> -	struct cdev cdev;		/* character device */
> +	struct cdev *cdev;		/* character device */
>  	struct device *parent;		/* device parent */
>  
>  	/* device info */
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux