Re: [PATCH 1/1] v4l: event: Add subscription to list before calling "add" operation

2018-11-05 Thread Dave Stevenson
Hi Sakari

On Mon, 5 Nov 2018 at 15:46, Sakari Ailus  wrote:
>
> Patch ad608fbcf166 changed how events were subscribed to address an issue
> elsewhere. As a side effect of that change, the "add" callback was called
> before the event subscription was added to the list of subscribed events,
> causing the first event (and possibly other events arriving soon
> afterwards) to be lost.
>
> Fix this by adding the subscription to the list before calling the "add"
> callback, and clean up afterwards if that fails.
>
> Fixes: ad608fbcf166 ("media: v4l: event: Prevent freeing event subscriptions 
> while accessed")
>
> Reported-by: Dave Stevenson 
> Signed-off-by: Sakari Ailus 
> ---
> Hi Dave, Hans,
>
> This should address the issue.
>
> The functions can (and probably should) be re-arranged later but let's get
> the fix right first.
>
> I haven't tested this using vivid yet as it crashes where I was testing
> it. I'll see later if it works elsewhere but I'm sending the patch for
> review in the meantime.

Thanks. That seems to fix the failure for my 3 drivers.

For what it's worth:
Tested-by: Dave Stevenson 

I haven't tested the failure path, but it looks sane.

>  drivers/media/v4l2-core/v4l2-event.c | 39 
> +++-
>  1 file changed, 25 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-event.c 
> b/drivers/media/v4l2-core/v4l2-event.c
> index a3ef1f50a4b3..2b6651aeb89b 100644
> --- a/drivers/media/v4l2-core/v4l2-event.c
> +++ b/drivers/media/v4l2-core/v4l2-event.c
> @@ -193,6 +193,22 @@ int v4l2_event_pending(struct v4l2_fh *fh)
>  }
>  EXPORT_SYMBOL_GPL(v4l2_event_pending);
>
> +static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev)
> +{
> +   struct v4l2_fh *fh = sev->fh;
> +   unsigned int i;
> +
> +   lockdep_assert_held(&fh->subscribe_lock);
> +   assert_spin_locked(&fh->vdev->fh_lock);
> +
> +   /* Remove any pending events for this subscription */
> +   for (i = 0; i < sev->in_use; i++) {
> +   list_del(&sev->events[sev_pos(sev, i)].list);
> +   fh->navailable--;
> +   }
> +   list_del(&sev->list);
> +}
> +
>  int v4l2_event_subscribe(struct v4l2_fh *fh,
>  const struct v4l2_event_subscription *sub, unsigned 
> elems,
>  const struct v4l2_subscribed_event_ops *ops)
> @@ -232,18 +248,20 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
> goto out_unlock;
> }
>
> +   spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> +   list_add(&sev->list, &fh->subscribed);
> +   spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> +
> if (sev->ops && sev->ops->add) {
> ret = sev->ops->add(sev, elems);
> if (ret) {
> +   spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> +   __v4l2_event_unsubscribe(sev);
> +   spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> kvfree(sev);
> -   goto out_unlock;
> }
> }
>
> -   spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> -   list_add(&sev->list, &fh->subscribed);
> -   spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> -
>  out_unlock:
> mutex_unlock(&fh->subscribe_lock);
>
> @@ -279,7 +297,6 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
>  {
> struct v4l2_subscribed_event *sev;
> unsigned long flags;
> -   int i;
>
> if (sub->type == V4L2_EVENT_ALL) {
> v4l2_event_unsubscribe_all(fh);
> @@ -291,14 +308,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
> spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>
> sev = v4l2_event_subscribed(fh, sub->type, sub->id);
> -   if (sev != NULL) {
> -   /* Remove any pending events for this subscription */
> -   for (i = 0; i < sev->in_use; i++) {
> -   list_del(&sev->events[sev_pos(sev, i)].list);
> -   fh->navailable--;
> -   }
> -   list_del(&sev->list);
> -   }
> +   if (sev != NULL)
> +   __v4l2_event_unsubscribe(sev);
>
> spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>
> --
> 2.11.0
>


Re: [PATCH 1/1] v4l: event: Add subscription to list before calling "add" operation

2018-11-05 Thread Hans Verkuil
On 11/05/2018 04:46 PM, Sakari Ailus wrote:
> Patch ad608fbcf166 changed how events were subscribed to address an issue
> elsewhere. As a side effect of that change, the "add" callback was called
> before the event subscription was added to the list of subscribed events,
> causing the first event (and possibly other events arriving soon
> afterwards) to be lost.
> 
> Fix this by adding the subscription to the list before calling the "add"
> callback, and clean up afterwards if that fails.
> 
> Fixes: ad608fbcf166 ("media: v4l: event: Prevent freeing event subscriptions 
> while accessed")
> 
> Reported-by: Dave Stevenson 
> Signed-off-by: Sakari Ailus 
> ---
> Hi Dave, Hans,
> 
> This should address the issue.
> 
> The functions can (and probably should) be re-arranged later but let's get
> the fix right first.
> 
> I haven't tested this using vivid yet as it crashes where I was testing
> it. I'll see later if it works elsewhere but I'm sending the patch for
> review in the meantime.

Did a quick vivid test and it passes the tests now.

I'll review the code tomorrow.

Regards,

Hans

> 
>  drivers/media/v4l2-core/v4l2-event.c | 39 
> +++-
>  1 file changed, 25 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-event.c 
> b/drivers/media/v4l2-core/v4l2-event.c
> index a3ef1f50a4b3..2b6651aeb89b 100644
> --- a/drivers/media/v4l2-core/v4l2-event.c
> +++ b/drivers/media/v4l2-core/v4l2-event.c
> @@ -193,6 +193,22 @@ int v4l2_event_pending(struct v4l2_fh *fh)
>  }
>  EXPORT_SYMBOL_GPL(v4l2_event_pending);
>  
> +static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev)
> +{
> + struct v4l2_fh *fh = sev->fh;
> + unsigned int i;
> +
> + lockdep_assert_held(&fh->subscribe_lock);
> + assert_spin_locked(&fh->vdev->fh_lock);
> +
> + /* Remove any pending events for this subscription */
> + for (i = 0; i < sev->in_use; i++) {
> + list_del(&sev->events[sev_pos(sev, i)].list);
> + fh->navailable--;
> + }
> + list_del(&sev->list);
> +}
> +
>  int v4l2_event_subscribe(struct v4l2_fh *fh,
>const struct v4l2_event_subscription *sub, unsigned 
> elems,
>const struct v4l2_subscribed_event_ops *ops)
> @@ -232,18 +248,20 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>   goto out_unlock;
>   }
>  
> + spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> + list_add(&sev->list, &fh->subscribed);
> + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> +
>   if (sev->ops && sev->ops->add) {
>   ret = sev->ops->add(sev, elems);
>   if (ret) {
> + spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> + __v4l2_event_unsubscribe(sev);
> + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>   kvfree(sev);
> - goto out_unlock;
>   }
>   }
>  
> - spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> - list_add(&sev->list, &fh->subscribed);
> - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> -
>  out_unlock:
>   mutex_unlock(&fh->subscribe_lock);
>  
> @@ -279,7 +297,6 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
>  {
>   struct v4l2_subscribed_event *sev;
>   unsigned long flags;
> - int i;
>  
>   if (sub->type == V4L2_EVENT_ALL) {
>   v4l2_event_unsubscribe_all(fh);
> @@ -291,14 +308,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
>   spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>  
>   sev = v4l2_event_subscribed(fh, sub->type, sub->id);
> - if (sev != NULL) {
> - /* Remove any pending events for this subscription */
> - for (i = 0; i < sev->in_use; i++) {
> - list_del(&sev->events[sev_pos(sev, i)].list);
> - fh->navailable--;
> - }
> - list_del(&sev->list);
> - }
> + if (sev != NULL)
> + __v4l2_event_unsubscribe(sev);
>  
>   spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>  
> 



[PATCH 1/1] v4l: event: Add subscription to list before calling "add" operation

2018-11-05 Thread Sakari Ailus
Patch ad608fbcf166 changed how events were subscribed to address an issue
elsewhere. As a side effect of that change, the "add" callback was called
before the event subscription was added to the list of subscribed events,
causing the first event (and possibly other events arriving soon
afterwards) to be lost.

Fix this by adding the subscription to the list before calling the "add"
callback, and clean up afterwards if that fails.

Fixes: ad608fbcf166 ("media: v4l: event: Prevent freeing event subscriptions 
while accessed")

Reported-by: Dave Stevenson 
Signed-off-by: Sakari Ailus 
---
Hi Dave, Hans,

This should address the issue.

The functions can (and probably should) be re-arranged later but let's get
the fix right first.

I haven't tested this using vivid yet as it crashes where I was testing
it. I'll see later if it works elsewhere but I'm sending the patch for
review in the meantime.

 drivers/media/v4l2-core/v4l2-event.c | 39 +++-
 1 file changed, 25 insertions(+), 14 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-event.c 
b/drivers/media/v4l2-core/v4l2-event.c
index a3ef1f50a4b3..2b6651aeb89b 100644
--- a/drivers/media/v4l2-core/v4l2-event.c
+++ b/drivers/media/v4l2-core/v4l2-event.c
@@ -193,6 +193,22 @@ int v4l2_event_pending(struct v4l2_fh *fh)
 }
 EXPORT_SYMBOL_GPL(v4l2_event_pending);
 
+static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev)
+{
+   struct v4l2_fh *fh = sev->fh;
+   unsigned int i;
+
+   lockdep_assert_held(&fh->subscribe_lock);
+   assert_spin_locked(&fh->vdev->fh_lock);
+
+   /* Remove any pending events for this subscription */
+   for (i = 0; i < sev->in_use; i++) {
+   list_del(&sev->events[sev_pos(sev, i)].list);
+   fh->navailable--;
+   }
+   list_del(&sev->list);
+}
+
 int v4l2_event_subscribe(struct v4l2_fh *fh,
 const struct v4l2_event_subscription *sub, unsigned 
elems,
 const struct v4l2_subscribed_event_ops *ops)
@@ -232,18 +248,20 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
goto out_unlock;
}
 
+   spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+   list_add(&sev->list, &fh->subscribed);
+   spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
if (sev->ops && sev->ops->add) {
ret = sev->ops->add(sev, elems);
if (ret) {
+   spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+   __v4l2_event_unsubscribe(sev);
+   spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
kvfree(sev);
-   goto out_unlock;
}
}
 
-   spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-   list_add(&sev->list, &fh->subscribed);
-   spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
 out_unlock:
mutex_unlock(&fh->subscribe_lock);
 
@@ -279,7 +297,6 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 {
struct v4l2_subscribed_event *sev;
unsigned long flags;
-   int i;
 
if (sub->type == V4L2_EVENT_ALL) {
v4l2_event_unsubscribe_all(fh);
@@ -291,14 +308,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 
sev = v4l2_event_subscribed(fh, sub->type, sub->id);
-   if (sev != NULL) {
-   /* Remove any pending events for this subscription */
-   for (i = 0; i < sev->in_use; i++) {
-   list_del(&sev->events[sev_pos(sev, i)].list);
-   fh->navailable--;
-   }
-   list_del(&sev->list);
-   }
+   if (sev != NULL)
+   __v4l2_event_unsubscribe(sev);
 
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
-- 
2.11.0