3.18-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Mauro Carvalho Chehab <mche...@s-opensource.com>

commit 76d81243a487c09619822ef8e7201a756e58a87d upstream.

As warned by smatch:
        drivers/media/dvb-core/dvb_frontend.c:314 dvb_frontend_get_event() 
warn: inconsistent returns 'sem:&fepriv->sem'.
          Locked on:   line 288
                       line 295
                       line 306
                       line 314
          Unlocked on: line 303

The lock implementation for get event is wrong, as, if an
interrupt occurs, down_interruptible() will fail, and the
routine will call up() twice when userspace calls the ioctl
again.

The bad code is there since when Linux migrated to git, in
2005.

Cc: sta...@vger.kernel.org
Signed-off-by: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 drivers/media/dvb-core/dvb_frontend.c |   23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -224,8 +224,20 @@ static void dvb_frontend_add_event(struc
        wake_up_interruptible (&events->wait_queue);
 }
 
+static int dvb_frontend_test_event(struct dvb_frontend_private *fepriv,
+                                  struct dvb_fe_events *events)
+{
+       int ret;
+
+       up(&fepriv->sem);
+       ret = events->eventw != events->eventr;
+       down(&fepriv->sem);
+
+       return ret;
+}
+
 static int dvb_frontend_get_event(struct dvb_frontend *fe,
-                           struct dvb_frontend_event *event, int flags)
+                                 struct dvb_frontend_event *event, int flags)
 {
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        struct dvb_fe_events *events = &fepriv->events;
@@ -243,13 +255,8 @@ static int dvb_frontend_get_event(struct
                if (flags & O_NONBLOCK)
                        return -EWOULDBLOCK;
 
-               up(&fepriv->sem);
-
-               ret = wait_event_interruptible (events->wait_queue,
-                                               events->eventw != 
events->eventr);
-
-               if (down_interruptible (&fepriv->sem))
-                       return -ERESTARTSYS;
+               ret = wait_event_interruptible(events->wait_queue,
+                                              dvb_frontend_test_event(fepriv, 
events));
 
                if (ret < 0)
                        return ret;


Reply via email to