Skip to content

Commit

Permalink
Merge tag 'media/v6.4-3' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/mchehab/linux-media

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
  • Loading branch information
Linus Torvalds committed May 18, 2023
2 parents 4d6d4c7 + 280a8ab commit b802651
Show file tree
Hide file tree
Showing 18 changed files with 293 additions and 59 deletions.
49 changes: 43 additions & 6 deletions drivers/media/dvb-core/dvb_ca_en50221.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions drivers/media/dvb-core/dvb_demux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down
69 changes: 56 additions & 13 deletions drivers/media/dvb-core/dvb_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -2761,17 +2780,22 @@ 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);
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
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;
}
Expand All @@ -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;
}
}
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -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:
Expand All @@ -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;
}

Expand All @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit b802651

Please sign in to comment.