Commit 649cfc2b authored by Philipp Zabel's avatar Philipp Zabel Committed by Mauro Carvalho Chehab
Browse files

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: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: default avatarHans Verkuil <hansverk@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent c45fbdf2
Loading
Loading
Loading
Loading
+10 −16
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/firmware.h>
#include <linux/gcd.h>
#include <linux/genalloc.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/io.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);
}

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
 */
@@ -2118,7 +2108,8 @@ static int coda_open(struct file *file)
{
	struct video_device *vdev = video_devdata(file);
	struct coda_dev *dev = video_get_drvdata(vdev);
	struct coda_ctx *ctx = NULL;
	struct coda_ctx *ctx;
	unsigned int max = ~0;
	char *name;
	int ret;
	int idx;
@@ -2127,12 +2118,13 @@ static int coda_open(struct file *file)
	if (!ctx)
		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) {
		ret = idx;
		goto err_coda_max;
	}
	set_bit(idx, &dev->instance_mask);

	name = kasprintf(GFP_KERNEL, "context%d", idx);
	if (!name) {
@@ -2241,8 +2233,8 @@ err_clk_per:
err_pm_get:
	v4l2_fh_del(&ctx->fh);
	v4l2_fh_exit(&ctx->fh);
	clear_bit(ctx->idx, &dev->instance_mask);
err_coda_name_init:
	ida_free(&dev->ida, ctx->idx);
err_coda_max:
	kfree(ctx);
	return ret;
@@ -2284,7 +2276,7 @@ static int coda_release(struct file *file)
	pm_runtime_put_sync(&dev->plat_dev->dev);
	v4l2_fh_del(&ctx->fh);
	v4l2_fh_exit(&ctx->fh);
	clear_bit(ctx->idx, &dev->instance_mask);
	ida_free(&dev->ida, ctx->idx);
	if (ctx->ops->release)
		ctx->ops->release(ctx);
	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->coda_mutex);
	ida_init(&dev->ida);

	dev->debugfs_root = debugfs_create_dir("coda", NULL);
	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->workbuf);
	debugfs_remove_recursive(dev->debugfs_root);
	ida_destroy(&dev->ida);
	return 0;
}

+2 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#define __CODA_H__

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