diff options
author | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2018-04-05 05:30:52 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-07-03 11:23:15 +0200 |
commit | dc00f08645be1f6a1319af6c6c1117137f787efc (patch) | |
tree | 73b1972219971c25a784019c06ad05b112822468 | |
parent | 1a4726ba1dedcdb92bc15a70e65150e9222ceed1 (diff) | |
download | linux-stable-dc00f08645be1f6a1319af6c6c1117137f787efc.tar.gz linux-stable-dc00f08645be1f6a1319af6c6c1117137f787efc.tar.bz2 linux-stable-dc00f08645be1f6a1319af6c6c1117137f787efc.zip |
media: dvb_frontend: fix locking issues at dvb_frontend_get_event()
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: stable@vger.kernel.org
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/media/dvb-core/dvb_frontend.c | 23 |
1 files changed, 15 insertions, 8 deletions
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 01511e5a5566..2f054db8807b 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -251,8 +251,20 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, 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; @@ -270,13 +282,8 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe, 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; |