From patchwork Wed Mar 25 05:37:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaedon Shin X-Patchwork-Id: 28968 X-Patchwork-Delegate: mchehab@kernel.org Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1Yae16-0000V3-Vn; Wed, 25 Mar 2015 06:38:13 +0100 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.76/mailfrontend-6) with esmtp id 1Yae14-0003Cb-5h; Wed, 25 Mar 2015 06:38:12 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752257AbbCYFhx (ORCPT + 1 other); Wed, 25 Mar 2015 01:37:53 -0400 Received: from mail-pa0-f44.google.com ([209.85.220.44]:36603 "EHLO mail-pa0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750710AbbCYFhw (ORCPT ); Wed, 25 Mar 2015 01:37:52 -0400 Received: by padcy3 with SMTP id cy3so17215964pad.3; Tue, 24 Mar 2015 22:37:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=F3Bwr89ZJqYmTi1y5KX01bTOWxjPy2XOOHvQDxZUhvA=; b=TQ5jPssE4eZiqQ8XJpTpLt244jyq6225qNjpgreWEyHHrT1n6uP4yHz7J2D3/YHbIC bPMxpxRwIGdrDHbguN2VadPzgyv+iBmx6ASeRLqGB5/ryXk9o/FHXFnALm0XkJSgcTDw u+cpNHKiLV3V9Jy9YtHLP9lRqahQ5+rRegNp3y7VPRWt7oylCDPFYMYLWgrA3RD3+QU8 u4IT1miT9pzRy5xbGKN//82K5hiKhzsElQ3s5FjogtVMeX+SdqEz6McBi6Q9PYRL5xtC s/1jF4WMUudxjhmqY/s0f+LWF/bioKQT6czGHwzXRIMyRDO/euKE/zqFFG6ixN5tYTej rlTQ== X-Received: by 10.68.69.5 with SMTP id a5mr13835413pbu.63.1427261872322; Tue, 24 Mar 2015 22:37:52 -0700 (PDT) Received: from praha.local.private ([211.255.134.165]) by mx.google.com with ESMTPSA id b12sm1074739pdl.62.2015.03.24.22.37.50 (version=TLSv1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 24 Mar 2015 22:37:51 -0700 (PDT) From: Jaedon Shin To: Mauro Carvalho Chehab Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, Jaedon Shin Subject: [PATCH RESEND] media: dmxdev: fix possible race condition Date: Wed, 25 Mar 2015 14:37:06 +0900 Message-Id: <1427261826-20208-1-git-send-email-jaedon.shin@gmail.com> X-Mailer: git-send-email 2.3.4 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 6.0.0.2142326, Antispam-Engine: 2.7.2.2107409, Antispam-Data: 2015.3.25.52724 X-PMX-Spam: Gauge=IIIIIIIII, Probability=9%, Report=' FORGED_FROM_GMAIL 0.1, MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, BODY_SIZE_5000_5999 0, BODY_SIZE_7000_LESS 0, DKIM_SIGNATURE 0, URI_ENDS_IN_HTML 0, __ANY_URI 0, __CP_URI_IN_BODY 0, __FRAUD_BODY_WEBMAIL 0, __FRAUD_WEBMAIL 0, __FRAUD_WEBMAIL_FROM 0, __FROM_GMAIL 0, __HAS_FROM 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __PHISH_SPEAR_STRUCTURE_1 0, __SANE_MSGID 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __URI_NO_WWW 0, __URI_NS , __YOUTUBE_RCVD 0' 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 --- 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; }