Commit b802651b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull media fixes from Mauro Carvalho Chehab:
 "Several fixes for the dvb core and drivers:

   - fix UAF and null pointer de-reference in DVB core

   - fix kernel runtime warning for blocking operation in wait_event*()
     in dvb core

   - fix write size bug in DVB conditional access core

   - fix dvb demux continuity counter debug check logic

   - randconfig build fixes in pvrusb2 and mn88443x

   - fix memory leak in ttusb-dec

   - fix netup_unidvb probe-time error check logic

   - improve error handling in dw2102 if it can't retrieve DVB MAC
     address"

* tag 'media/v6.4-3' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media:
  media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221
  media: dvb-core: Fix kernel WARNING for blocking operation in wait_event*()
  media: dvb-core: Fix use-after-free due to race at dvb_register_device()
  media: dvb-core: Fix use-after-free due on race condition at dvb_net
  media: dvb-core: Fix use-after-free on race condition at dvb_frontend
  media: mn88443x: fix !CONFIG_OF error by drop of_match_ptr from ID table
  media: ttusb-dec: fix memory leak in ttusb_dec_exit_dvb()
  media: dvb_ca_en50221: fix a size write bug
  media: netup_unidvb: fix irq init by register it at the end of probe
  media: dvb-usb: dw2102: fix uninit-value in su3000_read_mac_address
  media: dvb-usb: digitv: fix null-ptr-deref in digitv_i2c_xfer()
  media: dvb-usb-v2: rtl28xxu: fix null-ptr-deref in rtl28xxu_i2c_xfer
  media: dvb-usb-v2: ce6230: fix null-ptr-deref in ce6230_i2c_master_xfer()
  media: dvb-usb-v2: ec168: fix null-ptr-deref in ec168_i2c_xfer()
  media: dvb-usb: az6027: fix three null-ptr-deref in az6027_i2c_xfer()
  media: netup_unidvb: fix use-after-free at del_timer()
  media: dvb_demux: fix a bug for the continuity counter
  media: pvrusb2: fix DVB_CORE dependency
parents 4d6d4c7f 280a8ab8
Loading
Loading
Loading
Loading
+43 −6
Original line number Diff line number Diff line
@@ -151,6 +151,12 @@ struct dvb_ca_private {

	/* mutex serializing ioctls */
	struct mutex ioctl_mutex;

	/* A mutex used when a device is disconnected */
	struct mutex remove_mutex;

	/* Whether the device is disconnected */
	int exit;
};

static void dvb_ca_private_free(struct dvb_ca_private *ca)
@@ -187,7 +193,7 @@ static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
				    u8 *ebuf, int ecount);
static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
				     u8 *ebuf, int ecount);
				     u8 *ebuf, int ecount, int size_write_flag);

/**
 * findstr - Safely find needle in haystack.
@@ -370,7 +376,7 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
	ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10);
	if (ret)
		return ret;
	ret = dvb_ca_en50221_write_data(ca, slot, buf, 2);
	ret = dvb_ca_en50221_write_data(ca, slot, buf, 2, CMDREG_SW);
	if (ret != 2)
		return -EIO;
	ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
@@ -778,11 +784,13 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
 * @buf: The data in this buffer is treated as a complete link-level packet to
 *	 be written.
 * @bytes_write: Size of ebuf.
 * @size_write_flag: A flag on Command Register which says whether the link size
 * information will be writen or not.
 *
 * return: Number of bytes written, or < 0 on error.
 */
static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
				     u8 *buf, int bytes_write)
				     u8 *buf, int bytes_write, int size_write_flag)
{
	struct dvb_ca_slot *sl = &ca->slot_info[slot];
	int status;
@@ -817,7 +825,7 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,

	/* OK, set HC bit */
	status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
					    IRQEN | CMDREG_HC);
					    IRQEN | CMDREG_HC | size_write_flag);
	if (status)
		goto exit;

@@ -1508,7 +1516,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,

			mutex_lock(&sl->slot_lock);
			status = dvb_ca_en50221_write_data(ca, slot, fragbuf,
							   fraglen + 2);
							   fraglen + 2, 0);
			mutex_unlock(&sl->slot_lock);
			if (status == (fraglen + 2)) {
				written = 1;
@@ -1709,12 +1717,22 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)

	dprintk("%s\n", __func__);

	if (!try_module_get(ca->pub->owner))
	mutex_lock(&ca->remove_mutex);

	if (ca->exit) {
		mutex_unlock(&ca->remove_mutex);
		return -ENODEV;
	}

	if (!try_module_get(ca->pub->owner)) {
		mutex_unlock(&ca->remove_mutex);
		return -EIO;
	}

	err = dvb_generic_open(inode, file);
	if (err < 0) {
		module_put(ca->pub->owner);
		mutex_unlock(&ca->remove_mutex);
		return err;
	}

@@ -1739,6 +1757,7 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)

	dvb_ca_private_get(ca);

	mutex_unlock(&ca->remove_mutex);
	return 0;
}

@@ -1758,6 +1777,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)

	dprintk("%s\n", __func__);

	mutex_lock(&ca->remove_mutex);

	/* mark the CA device as closed */
	ca->open = 0;
	dvb_ca_en50221_thread_update_delay(ca);
@@ -1768,6 +1789,13 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)

	dvb_ca_private_put(ca);

	if (dvbdev->users == 1 && ca->exit == 1) {
		mutex_unlock(&ca->remove_mutex);
		wake_up(&dvbdev->wait_queue);
	} else {
		mutex_unlock(&ca->remove_mutex);
	}

	return err;
}

@@ -1891,6 +1919,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
	}

	mutex_init(&ca->ioctl_mutex);
	mutex_init(&ca->remove_mutex);

	if (signal_pending(current)) {
		ret = -EINTR;
@@ -1933,6 +1962,14 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)

	dprintk("%s\n", __func__);

	mutex_lock(&ca->remove_mutex);
	ca->exit = 1;
	mutex_unlock(&ca->remove_mutex);

	if (ca->dvbdev->users < 1)
		wait_event(ca->dvbdev->wait_queue,
				ca->dvbdev->users == 1);

	/* shutdown the thread if there was one */
	kthread_stop(ca->thread);

+2 −2
Original line number Diff line number Diff line
@@ -115,12 +115,12 @@ static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,

	cc = buf[3] & 0x0f;
	ccok = ((feed->cc + 1) & 0x0f) == cc;
	feed->cc = cc;
	if (!ccok) {
		set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
		dprintk_sect_loss("missed packet: %d instead of %d!\n",
				  cc, (feed->cc + 1) & 0x0f);
	}
	feed->cc = cc;

	if (buf[1] & 0x40)	// PUSI ?
		feed->peslen = 0xfffa;
@@ -300,7 +300,6 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,

	cc = buf[3] & 0x0f;
	ccok = ((feed->cc + 1) & 0x0f) == cc;
	feed->cc = cc;

	if (buf[3] & 0x20) {
		/* adaption field present, check for discontinuity_indicator */
@@ -336,6 +335,7 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
		feed->pusi_seen = false;
		dvb_dmx_swfilter_section_new(feed);
	}
	feed->cc = cc;

	if (buf[1] & 0x40) {
		/* PUSI=1 (is set), section boundary is here */
+56 −13
Original line number Diff line number Diff line
@@ -293,14 +293,22 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
	}

	if (events->eventw == events->eventr) {
		int ret;
		struct wait_queue_entry wait;
		int ret = 0;

		if (flags & O_NONBLOCK)
			return -EWOULDBLOCK;

		ret = wait_event_interruptible(events->wait_queue,
					       dvb_frontend_test_event(fepriv, events));

		init_waitqueue_entry(&wait, current);
		add_wait_queue(&events->wait_queue, &wait);
		while (!dvb_frontend_test_event(fepriv, events)) {
			wait_woken(&wait, TASK_INTERRUPTIBLE, 0);
			if (signal_pending(current)) {
				ret = -ERESTARTSYS;
				break;
			}
		}
		remove_wait_queue(&events->wait_queue, &wait);
		if (ret < 0)
			return ret;
	}
@@ -809,15 +817,26 @@ static void dvb_frontend_stop(struct dvb_frontend *fe)

	dev_dbg(fe->dvb->device, "%s:\n", __func__);

	mutex_lock(&fe->remove_mutex);

	if (fe->exit != DVB_FE_DEVICE_REMOVED)
		fe->exit = DVB_FE_NORMAL_EXIT;
	mb();

	if (!fepriv->thread)
	if (!fepriv->thread) {
		mutex_unlock(&fe->remove_mutex);
		return;
	}

	kthread_stop(fepriv->thread);

	mutex_unlock(&fe->remove_mutex);

	if (fepriv->dvbdev->users < -1) {
		wait_event(fepriv->dvbdev->wait_queue,
			   fepriv->dvbdev->users == -1);
	}

	sema_init(&fepriv->sem, 1);
	fepriv->state = FESTATE_IDLE;

@@ -2761,9 +2780,13 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
	struct dvb_adapter *adapter = fe->dvb;
	int ret;

	mutex_lock(&fe->remove_mutex);

	dev_dbg(fe->dvb->device, "%s:\n", __func__);
	if (fe->exit == DVB_FE_DEVICE_REMOVED)
		return -ENODEV;
	if (fe->exit == DVB_FE_DEVICE_REMOVED) {
		ret = -ENODEV;
		goto err_remove_mutex;
	}

	if (adapter->mfe_shared == 2) {
		mutex_lock(&adapter->mfe_lock);
@@ -2771,7 +2794,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
			if (adapter->mfe_dvbdev &&
			    !adapter->mfe_dvbdev->writers) {
				mutex_unlock(&adapter->mfe_lock);
				return -EBUSY;
				ret = -EBUSY;
				goto err_remove_mutex;
			}
			adapter->mfe_dvbdev = dvbdev;
		}
@@ -2794,8 +2818,10 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
			while (mferetry-- && (mfedev->users != -1 ||
					      mfepriv->thread)) {
				if (msleep_interruptible(500)) {
					if (signal_pending(current))
						return -EINTR;
					if (signal_pending(current)) {
						ret = -EINTR;
						goto err_remove_mutex;
					}
				}
			}

@@ -2807,7 +2833,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
				if (mfedev->users != -1 ||
				    mfepriv->thread) {
					mutex_unlock(&adapter->mfe_lock);
					return -EBUSY;
					ret = -EBUSY;
					goto err_remove_mutex;
				}
				adapter->mfe_dvbdev = dvbdev;
			}
@@ -2866,6 +2893,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)

	if (adapter->mfe_shared)
		mutex_unlock(&adapter->mfe_lock);

	mutex_unlock(&fe->remove_mutex);
	return ret;

err3:
@@ -2887,6 +2916,9 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
err0:
	if (adapter->mfe_shared)
		mutex_unlock(&adapter->mfe_lock);

err_remove_mutex:
	mutex_unlock(&fe->remove_mutex);
	return ret;
}

@@ -2897,6 +2929,8 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
	struct dvb_frontend_private *fepriv = fe->frontend_priv;
	int ret;

	mutex_lock(&fe->remove_mutex);

	dev_dbg(fe->dvb->device, "%s:\n", __func__);

	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
@@ -2918,10 +2952,18 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
		}
		mutex_unlock(&fe->dvb->mdev_lock);
#endif
		if (fe->exit != DVB_FE_NO_EXIT)
			wake_up(&dvbdev->wait_queue);
		if (fe->ops.ts_bus_ctrl)
			fe->ops.ts_bus_ctrl(fe, 0);

		if (fe->exit != DVB_FE_NO_EXIT) {
			mutex_unlock(&fe->remove_mutex);
			wake_up(&dvbdev->wait_queue);
		} else {
			mutex_unlock(&fe->remove_mutex);
		}

	} else {
		mutex_unlock(&fe->remove_mutex);
	}

	dvb_frontend_put(fe);
@@ -3022,6 +3064,7 @@ int dvb_register_frontend(struct dvb_adapter *dvb,
	fepriv = fe->frontend_priv;

	kref_init(&fe->refcount);
	mutex_init(&fe->remove_mutex);

	/*
	 * After initialization, there need to be two references: one
+35 −3
Original line number Diff line number Diff line
@@ -1564,15 +1564,43 @@ static long dvb_net_ioctl(struct file *file,
	return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl);
}

static int locked_dvb_net_open(struct inode *inode, struct file *file)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_net *dvbnet = dvbdev->priv;
	int ret;

	if (mutex_lock_interruptible(&dvbnet->remove_mutex))
		return -ERESTARTSYS;

	if (dvbnet->exit) {
		mutex_unlock(&dvbnet->remove_mutex);
		return -ENODEV;
	}

	ret = dvb_generic_open(inode, file);

	mutex_unlock(&dvbnet->remove_mutex);

	return ret;
}

static int dvb_net_close(struct inode *inode, struct file *file)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_net *dvbnet = dvbdev->priv;

	mutex_lock(&dvbnet->remove_mutex);

	dvb_generic_release(inode, file);

	if(dvbdev->users == 1 && dvbnet->exit == 1)
	if (dvbdev->users == 1 && dvbnet->exit == 1) {
		mutex_unlock(&dvbnet->remove_mutex);
		wake_up(&dvbdev->wait_queue);
	} else {
		mutex_unlock(&dvbnet->remove_mutex);
	}

	return 0;
}

@@ -1580,7 +1608,7 @@ static int dvb_net_close(struct inode *inode, struct file *file)
static const struct file_operations dvb_net_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = dvb_net_ioctl,
	.open =	dvb_generic_open,
	.open =	locked_dvb_net_open,
	.release = dvb_net_close,
	.llseek = noop_llseek,
};
@@ -1599,7 +1627,10 @@ void dvb_net_release (struct dvb_net *dvbnet)
{
	int i;

	mutex_lock(&dvbnet->remove_mutex);
	dvbnet->exit = 1;
	mutex_unlock(&dvbnet->remove_mutex);

	if (dvbnet->dvbdev->users < 1)
		wait_event(dvbnet->dvbdev->wait_queue,
				dvbnet->dvbdev->users == 1);
@@ -1621,6 +1652,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
	int i;

	mutex_init(&dvbnet->ioctl_mutex);
	mutex_init(&dvbnet->remove_mutex);
	dvbnet->demux = dmx;

	for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+63 −21
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <media/tuner.h>

static DEFINE_MUTEX(dvbdev_mutex);
static LIST_HEAD(dvbdevfops_list);
static int dvbdev_debug;

module_param(dvbdev_debug, int, 0644);
@@ -453,7 +454,8 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
			enum dvb_device_type type, int demux_sink_pads)
{
	struct dvb_device *dvbdev;
	struct file_operations *dvbdevfops;
	struct file_operations *dvbdevfops = NULL;
	struct dvbdevfops_node *node = NULL, *new_node = NULL;
	struct device *clsdev;
	int minor;
	int id, ret;
@@ -468,20 +470,47 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
	}

	*pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL);

	if (!dvbdev){
		mutex_unlock(&dvbdev_register_lock);
		return -ENOMEM;
	}

	dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
	/*
	 * When a device of the same type is probe()d more than once,
	 * the first allocated fops are used. This prevents memory leaks
	 * that can occur when the same device is probe()d repeatedly.
	 */
	list_for_each_entry(node, &dvbdevfops_list, list_head) {
		if (node->fops->owner == adap->module &&
				node->type == type &&
				node->template == template) {
			dvbdevfops = node->fops;
			break;
		}
	}

	if (dvbdevfops == NULL) {
		dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
		if (!dvbdevfops) {
			kfree(dvbdev);
			mutex_unlock(&dvbdev_register_lock);
			return -ENOMEM;
		}

		new_node = kzalloc(sizeof(struct dvbdevfops_node), GFP_KERNEL);
		if (!new_node) {
			kfree(dvbdevfops);
			kfree(dvbdev);
			mutex_unlock(&dvbdev_register_lock);
			return -ENOMEM;
		}

		new_node->fops = dvbdevfops;
		new_node->type = type;
		new_node->template = template;
		list_add_tail (&new_node->list_head, &dvbdevfops_list);
	}

	memcpy(dvbdev, template, sizeof(struct dvb_device));
	kref_init(&dvbdev->ref);
	dvbdev->type = type;
@@ -490,20 +519,20 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
	dvbdev->priv = priv;
	dvbdev->fops = dvbdevfops;
	init_waitqueue_head (&dvbdev->wait_queue);

	dvbdevfops->owner = adap->module;

	list_add_tail (&dvbdev->list_head, &adap->device_list);

	down_write(&minor_rwsem);
#ifdef CONFIG_DVB_DYNAMIC_MINORS
	for (minor = 0; minor < MAX_DVB_MINORS; minor++)
		if (dvb_minors[minor] == NULL)
			break;

	if (minor == MAX_DVB_MINORS) {
		list_del (&dvbdev->list_head);
		if (new_node) {
			list_del (&new_node->list_head);
			kfree(dvbdevfops);
			kfree(new_node);
		}
		list_del (&dvbdev->list_head);
		kfree(dvbdev);
		up_write(&minor_rwsem);
		mutex_unlock(&dvbdev_register_lock);
@@ -512,41 +541,47 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
#else
	minor = nums2minor(adap->num, type, id);
#endif

	dvbdev->minor = minor;
	dvb_minors[minor] = dvb_device_get(dvbdev);
	up_write(&minor_rwsem);

	ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads);
	if (ret) {
		pr_err("%s: dvb_register_media_device failed to create the mediagraph\n",
		      __func__);

		if (new_node) {
			list_del (&new_node->list_head);
			kfree(dvbdevfops);
			kfree(new_node);
		}
		dvb_media_device_free(dvbdev);
		list_del (&dvbdev->list_head);
		kfree(dvbdevfops);
		kfree(dvbdev);
		mutex_unlock(&dvbdev_register_lock);
		return ret;
	}

	mutex_unlock(&dvbdev_register_lock);

	clsdev = device_create(dvb_class, adap->device,
			       MKDEV(DVB_MAJOR, minor),
			       dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
	if (IS_ERR(clsdev)) {
		pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n",
		       __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
		if (new_node) {
			list_del (&new_node->list_head);
			kfree(dvbdevfops);
			kfree(new_node);
		}
		dvb_media_device_free(dvbdev);
		list_del (&dvbdev->list_head);
		kfree(dvbdevfops);
		kfree(dvbdev);
		mutex_unlock(&dvbdev_register_lock);
		return PTR_ERR(clsdev);
	}

	dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
		adap->num, dnames[type], id, minor, minor);

	mutex_unlock(&dvbdev_register_lock);
	return 0;
}
EXPORT_SYMBOL(dvb_register_device);
@@ -575,7 +610,6 @@ static void dvb_free_device(struct kref *ref)
{
	struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref);

	kfree (dvbdev->fops);
	kfree (dvbdev);
}

@@ -1081,9 +1115,17 @@ static int __init init_dvbdev(void)

static void __exit exit_dvbdev(void)
{
	struct dvbdevfops_node *node, *next;

	class_destroy(dvb_class);
	cdev_del(&dvb_device_cdev);
	unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);

	list_for_each_entry_safe(node, next, &dvbdevfops_list, list_head) {
		list_del (&node->list_head);
		kfree(node->fops);
		kfree(node);
	}
}

subsys_initcall(init_dvbdev);
Loading