This patch fixes race condition between dvb_dmxdev_buffer_read and dvb_demux_io_ioctl.
There are race conditions executing DMX_ADD_PID or DMX_REMOVE_PID in the dvb_demux_ioctl when dvb_demux_read is waiting for the data by wait_event_interruptible. So, this fixes to sleep with acquired mutex and splits dvb_dmxdev_buffer_read into dvb_dvr_read. Signed-off-by: Jaedon Shin <jaedon.s...@gmail.com> --- drivers/media/dvb-core/dmxdev.c | 94 ++++++++++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index abff803ad69a..c2564b0e389b 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -57,10 +57,11 @@ static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf, return dvb_ringbuffer_write(buf, src, len); } -static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src, +static ssize_t dvb_dmxdev_buffer_read(struct dmxdev_filter *dmxdevfilter, int non_blocking, char __user *buf, size_t count, loff_t *ppos) { + struct dvb_ringbuffer *src = &dmxdevfilter->buffer; size_t todo; ssize_t avail; ssize_t ret = 0; @@ -75,16 +76,21 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src, } for (todo = count; todo > 0; todo -= ret) { - if (non_blocking && dvb_ringbuffer_empty(src)) { - ret = -EWOULDBLOCK; - break; - } + if (dvb_ringbuffer_empty(src)) { + mutex_unlock(&dmxdevfilter->mutex); - ret = wait_event_interruptible(src->queue, - !dvb_ringbuffer_empty(src) || - (src->error != 0)); - if (ret < 0) - break; + if (non_blocking) + return -EWOULDBLOCK; + + ret = wait_event_interruptible(src->queue, + !dvb_ringbuffer_empty(src) || + (src->error != 0)); + if (ret < 0) + return ret; + + if (mutex_lock_interruptible(&dmxdevfilter->mutex)) + return -ERESTARTSYS; + } if (src->error) { ret = src->error; @@ -242,13 +248,63 @@ static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count, { struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; + struct dvb_ringbuffer *src = &dmxdev->dvr_buffer; + size_t todo; + ssize_t avail; + ssize_t ret = 0; - if (dmxdev->exit) + if (mutex_lock_interruptible(&dmxdev->mutex)) + return -ERESTARTSYS; + + if (dmxdev->exit) { + mutex_unlock(&dmxdev->mutex); return -ENODEV; + } + + if (src->error) { + ret = src->error; + dvb_ringbuffer_flush(src); + mutex_unlock(&dmxdev->mutex); + return ret; + } + + for (todo = count; todo > 0; todo -= ret) { + if (dvb_ringbuffer_empty(src)) { + mutex_unlock(&dmxdev->mutex); - return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, - file->f_flags & O_NONBLOCK, - buf, count, ppos); + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + ret = wait_event_interruptible(src->queue, + !dvb_ringbuffer_empty(src) || + (src->error != 0)); + if (ret < 0) + return ret; + + if (mutex_lock_interruptible(&dmxdev->mutex)) + return -ERESTARTSYS; + } + + if (src->error) { + ret = src->error; + dvb_ringbuffer_flush(src); + break; + } + + avail = dvb_ringbuffer_avail(src); + if (avail > todo) + avail = todo; + + ret = dvb_ringbuffer_read_user(src, buf, avail); + if (ret < 0) + break; + + buf += ret; + } + + mutex_unlock(&dmxdev->mutex); + + return (count - todo) ? (count - todo) : ret; } static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev, @@ -283,7 +339,6 @@ static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev, return 0; } - static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter *dmxdevfilter, int state) { @@ -904,7 +959,7 @@ static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil, hcount = 3 + dfil->todo; if (hcount > count) hcount = count; - result = dvb_dmxdev_buffer_read(&dfil->buffer, + result = dvb_dmxdev_buffer_read(dfil, file->f_flags & O_NONBLOCK, buf, hcount, ppos); if (result < 0) { @@ -925,7 +980,7 @@ static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil, } if (count > dfil->todo) count = dfil->todo; - result = dvb_dmxdev_buffer_read(&dfil->buffer, + result = dvb_dmxdev_buffer_read(dfil, file->f_flags & O_NONBLOCK, buf, count, ppos); if (result < 0) @@ -947,11 +1002,12 @@ dvb_demux_read(struct file *file, char __user *buf, size_t count, if (dmxdevfilter->type == DMXDEV_TYPE_SEC) ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos); else - ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer, + ret = dvb_dmxdev_buffer_read(dmxdevfilter, file->f_flags & O_NONBLOCK, buf, count, ppos); - mutex_unlock(&dmxdevfilter->mutex); + if (mutex_is_locked(&dmxdevfilter->mutex)) + mutex_unlock(&dmxdevfilter->mutex); return ret; } -- 2.3.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/