media: coda: fix memory corruption in case more than 32 instances are opened

The ffz() return value is undefined if the instance mask does not
contain any zeros. If it returned 32, the following set_bit would
corrupt the debugfs_root pointer.
Switch to IDA for context index allocation. This also removes the
artificial 32 instance limit for all except CodaDx6.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Hans Verkuil <hansverk@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
Philipp Zabel 2018-11-06 05:40:54 -05:00 committed by Mauro Carvalho Chehab
parent c45fbdf24c
commit 649cfc2bdf
2 changed files with 12 additions and 17 deletions

View File

@ -17,6 +17,7 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/gcd.h> #include <linux/gcd.h>
#include <linux/genalloc.h> #include <linux/genalloc.h>
#include <linux/idr.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
@ -2099,17 +2100,6 @@ int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
return coda_queue_init(priv, dst_vq); return coda_queue_init(priv, dst_vq);
} }
static int coda_next_free_instance(struct coda_dev *dev)
{
int idx = ffz(dev->instance_mask);
if ((idx < 0) ||
(dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
return -EBUSY;
return idx;
}
/* /*
* File operations * File operations
*/ */
@ -2118,7 +2108,8 @@ static int coda_open(struct file *file)
{ {
struct video_device *vdev = video_devdata(file); struct video_device *vdev = video_devdata(file);
struct coda_dev *dev = video_get_drvdata(vdev); struct coda_dev *dev = video_get_drvdata(vdev);
struct coda_ctx *ctx = NULL; struct coda_ctx *ctx;
unsigned int max = ~0;
char *name; char *name;
int ret; int ret;
int idx; int idx;
@ -2127,12 +2118,13 @@ static int coda_open(struct file *file)
if (!ctx) if (!ctx)
return -ENOMEM; return -ENOMEM;
idx = coda_next_free_instance(dev); if (dev->devtype->product == CODA_DX6)
max = CODADX6_MAX_INSTANCES - 1;
idx = ida_alloc_max(&dev->ida, max, GFP_KERNEL);
if (idx < 0) { if (idx < 0) {
ret = idx; ret = idx;
goto err_coda_max; goto err_coda_max;
} }
set_bit(idx, &dev->instance_mask);
name = kasprintf(GFP_KERNEL, "context%d", idx); name = kasprintf(GFP_KERNEL, "context%d", idx);
if (!name) { if (!name) {
@ -2241,8 +2233,8 @@ err_clk_per:
err_pm_get: err_pm_get:
v4l2_fh_del(&ctx->fh); v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh); v4l2_fh_exit(&ctx->fh);
clear_bit(ctx->idx, &dev->instance_mask);
err_coda_name_init: err_coda_name_init:
ida_free(&dev->ida, ctx->idx);
err_coda_max: err_coda_max:
kfree(ctx); kfree(ctx);
return ret; return ret;
@ -2284,7 +2276,7 @@ static int coda_release(struct file *file)
pm_runtime_put_sync(&dev->plat_dev->dev); pm_runtime_put_sync(&dev->plat_dev->dev);
v4l2_fh_del(&ctx->fh); v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh); v4l2_fh_exit(&ctx->fh);
clear_bit(ctx->idx, &dev->instance_mask); ida_free(&dev->ida, ctx->idx);
if (ctx->ops->release) if (ctx->ops->release)
ctx->ops->release(ctx); ctx->ops->release(ctx);
debugfs_remove_recursive(ctx->debugfs_entry); debugfs_remove_recursive(ctx->debugfs_entry);
@ -2745,6 +2737,7 @@ static int coda_probe(struct platform_device *pdev)
mutex_init(&dev->dev_mutex); mutex_init(&dev->dev_mutex);
mutex_init(&dev->coda_mutex); mutex_init(&dev->coda_mutex);
ida_init(&dev->ida);
dev->debugfs_root = debugfs_create_dir("coda", NULL); dev->debugfs_root = debugfs_create_dir("coda", NULL);
if (!dev->debugfs_root) if (!dev->debugfs_root)
@ -2832,6 +2825,7 @@ static int coda_remove(struct platform_device *pdev)
coda_free_aux_buf(dev, &dev->tempbuf); coda_free_aux_buf(dev, &dev->tempbuf);
coda_free_aux_buf(dev, &dev->workbuf); coda_free_aux_buf(dev, &dev->workbuf);
debugfs_remove_recursive(dev->debugfs_root); debugfs_remove_recursive(dev->debugfs_root);
ida_destroy(&dev->ida);
return 0; return 0;
} }

View File

@ -16,6 +16,7 @@
#define __CODA_H__ #define __CODA_H__
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/idr.h>
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
@ -95,7 +96,7 @@ struct coda_dev {
struct workqueue_struct *workqueue; struct workqueue_struct *workqueue;
struct v4l2_m2m_dev *m2m_dev; struct v4l2_m2m_dev *m2m_dev;
struct list_head instances; struct list_head instances;
unsigned long instance_mask; struct ida ida;
struct dentry *debugfs_root; struct dentry *debugfs_root;
}; };