diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2013-04-25 21:28:51 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-06-08 18:51:16 -0400 |
commit | 17706f5653a90ff277b5b36c2eb60ff872df5e7a (patch) | |
tree | 110fbcca442fc9801187db3b73de32897fe9b9a0 /drivers/media/usb/uvc/uvc_v4l2.c | |
parent | c2a273b24f2c82184cc0f04ba3a61da51c84724b (diff) |
[media] uvcvideo: Fix open/close race condition
Maintaining the users count using an atomic variable makes sure that
access to the counter won't be racy, but doesn't serialize access to the
operations protected by the counter. This creates a race condition that
could result in the status URB being submitted multiple times.
Use a mutex to protect the users count and serialize access to the
status start and stop operations.
Reported-by: Shawn Nematbakhsh <shawnn@chromium.org>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/usb/uvc/uvc_v4l2.c')
-rw-r--r-- | drivers/media/usb/uvc/uvc_v4l2.c | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index b2dc32623a71..3afff92804d3 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c | |||
@@ -498,16 +498,20 @@ static int uvc_v4l2_open(struct file *file) | |||
498 | return -ENOMEM; | 498 | return -ENOMEM; |
499 | } | 499 | } |
500 | 500 | ||
501 | if (atomic_inc_return(&stream->dev->users) == 1) { | 501 | mutex_lock(&stream->dev->lock); |
502 | ret = uvc_status_start(stream->dev); | 502 | if (stream->dev->users == 0) { |
503 | ret = uvc_status_start(stream->dev, GFP_KERNEL); | ||
503 | if (ret < 0) { | 504 | if (ret < 0) { |
504 | atomic_dec(&stream->dev->users); | 505 | mutex_unlock(&stream->dev->lock); |
505 | usb_autopm_put_interface(stream->dev->intf); | 506 | usb_autopm_put_interface(stream->dev->intf); |
506 | kfree(handle); | 507 | kfree(handle); |
507 | return ret; | 508 | return ret; |
508 | } | 509 | } |
509 | } | 510 | } |
510 | 511 | ||
512 | stream->dev->users++; | ||
513 | mutex_unlock(&stream->dev->lock); | ||
514 | |||
511 | v4l2_fh_init(&handle->vfh, stream->vdev); | 515 | v4l2_fh_init(&handle->vfh, stream->vdev); |
512 | v4l2_fh_add(&handle->vfh); | 516 | v4l2_fh_add(&handle->vfh); |
513 | handle->chain = stream->chain; | 517 | handle->chain = stream->chain; |
@@ -538,8 +542,10 @@ static int uvc_v4l2_release(struct file *file) | |||
538 | kfree(handle); | 542 | kfree(handle); |
539 | file->private_data = NULL; | 543 | file->private_data = NULL; |
540 | 544 | ||
541 | if (atomic_dec_return(&stream->dev->users) == 0) | 545 | mutex_lock(&stream->dev->lock); |
546 | if (--stream->dev->users == 0) | ||
542 | uvc_status_stop(stream->dev); | 547 | uvc_status_stop(stream->dev); |
548 | mutex_unlock(&stream->dev->lock); | ||
543 | 549 | ||
544 | usb_autopm_put_interface(stream->dev->intf); | 550 | usb_autopm_put_interface(stream->dev->intf); |
545 | return 0; | 551 | return 0; |