Skip to content

Commit

Permalink
media: v4l: event: Prevent freeing event subscriptions while accessed
Browse files Browse the repository at this point in the history
commit ad608fb upstream.

The event subscriptions are added to the subscribed event list while
holding a spinlock, but that lock is subsequently released while still
accessing the subscription object. This makes it possible to unsubscribe
the event --- and freeing the subscription object's memory --- while
the subscription object is simultaneously accessed.

Prevent this by adding a mutex to serialise the event subscription and
unsubscription. This also gives a guarantee to the callback ops that the
add op has returned before the del op is called.

This change also results in making the elems field less special:
subscriptions are only added to the event list once they are fully
initialised.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: stable@vger.kernel.org # for 4.14 and up
Fixes: c3b5b02 ("V4L/DVB: V4L: Events: Add backend")
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Sakari Ailus authored and Greg Kroah-Hartman committed Oct 4, 2018
1 parent fcaca55 commit d61ba34
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 18 deletions.
38 changes: 20 additions & 18 deletions drivers/media/v4l2-core/v4l2-event.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,6 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
if (sev == NULL)
return;

/*
* If the event has been added to the fh->subscribed list, but its
* add op has not completed yet elems will be 0, treat this as
* not being subscribed.
*/
if (!sev->elems)
return;

/* Increase event sequence number on fh. */
fh->sequence++;

Expand Down Expand Up @@ -208,6 +200,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
struct v4l2_subscribed_event *sev, *found_ev;
unsigned long flags;
unsigned i;
int ret = 0;

if (sub->type == V4L2_EVENT_ALL)
return -EINVAL;
Expand All @@ -226,31 +219,36 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
sev->flags = sub->flags;
sev->fh = fh;
sev->ops = ops;
sev->elems = elems;

mutex_lock(&fh->subscribe_lock);

spin_lock_irqsave(&fh->vdev->fh_lock, flags);
found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
if (!found_ev)
list_add(&sev->list, &fh->subscribed);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);

if (found_ev) {
/* Already listening */
kvfree(sev);
return 0; /* Already listening */
goto out_unlock;
}

if (sev->ops && sev->ops->add) {
int ret = sev->ops->add(sev, elems);
ret = sev->ops->add(sev, elems);
if (ret) {
sev->ops = NULL;
v4l2_event_unsubscribe(fh, sub);
return ret;
kvfree(sev);
goto out_unlock;
}
}

/* Mark as ready for use */
sev->elems = elems;
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
list_add(&sev->list, &fh->subscribed);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);

return 0;
out_unlock:
mutex_unlock(&fh->subscribe_lock);

return ret;
}
EXPORT_SYMBOL_GPL(v4l2_event_subscribe);

Expand Down Expand Up @@ -289,6 +287,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
return 0;
}

mutex_lock(&fh->subscribe_lock);

spin_lock_irqsave(&fh->vdev->fh_lock, flags);

sev = v4l2_event_subscribed(fh, sub->type, sub->id);
Expand All @@ -306,6 +306,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
if (sev && sev->ops && sev->ops->del)
sev->ops->del(sev);

mutex_unlock(&fh->subscribe_lock);

kvfree(sev);

return 0;
Expand Down
2 changes: 2 additions & 0 deletions drivers/media/v4l2-core/v4l2-fh.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
INIT_LIST_HEAD(&fh->available);
INIT_LIST_HEAD(&fh->subscribed);
fh->sequence = -1;
mutex_init(&fh->subscribe_lock);
}
EXPORT_SYMBOL_GPL(v4l2_fh_init);

Expand Down Expand Up @@ -90,6 +91,7 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
return;
v4l_disable_media_source(fh->vdev);
v4l2_event_unsubscribe_all(fh);
mutex_destroy(&fh->subscribe_lock);
fh->vdev = NULL;
}
EXPORT_SYMBOL_GPL(v4l2_fh_exit);
Expand Down
4 changes: 4 additions & 0 deletions include/media/v4l2-fh.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ struct v4l2_ctrl_handler;
* @prio: priority of the file handler, as defined by &enum v4l2_priority
*
* @wait: event' s wait queue
* @subscribe_lock: serialise changes to the subscribed list; guarantee that
* the add and del event callbacks are orderly called
* @subscribed: list of subscribed events
* @available: list of events waiting to be dequeued
* @navailable: number of available events at @available list
* @sequence: event sequence number
*
* @m2m_ctx: pointer to &struct v4l2_m2m_ctx
*/
struct v4l2_fh {
Expand All @@ -51,6 +54,7 @@ struct v4l2_fh {

/* Events */
wait_queue_head_t wait;
struct mutex subscribe_lock;
struct list_head subscribed;
struct list_head available;
unsigned int navailable;
Expand Down

0 comments on commit d61ba34

Please sign in to comment.